您的位置:首页 > 产品设计 > UI/UE

Hdu 3726 Graph and Queries(并查集+平衡树+启发式合并)

2015-05-19 21:25 507 查看
题目链接

Graph and Queries

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 2649 Accepted Submission(s): 600



[align=left]Problem Description[/align]
You are given an undirected graph with N vertexes and M edges. Every vertex in this graph has an integer value assigned to it at the beginning. You're also given a sequence of operations and you need to process them as requested.
Here's a list of the possible operations that you might encounter:

1) Deletes an edge from the graph.

The format is [D X], where X is an integer from 1 to M, indicating the ID of the edge that you should delete. It is guaranteed that no edge will be deleted more than once.

2) Queries the weight of the vertex with K-th maximum value among all vertexes currently connected with vertex X (including X itself).

The format is [Q X K], where X is an integer from 1 to N, indicating the id of the vertex, and you may assume that K will always fit into a 32-bit signed integer. In case K is illegal, the value for that query will be considered as undefined, and you should
return 0 as the answer to that query.

3) Changes the weight of a vertex.

The format is [C X V], where X is an integer from 1 to N, and V is an integer within the range [-106, 106].

The operations end with one single character, E, which indicates that the current case has ended.

For simplicity, you only need to output one real number - the average answer of all queries.

[align=left]Input[/align]
There are multiple test cases in the input file. Each case starts with two integers N and M (1 <= N <= 2 * 104, 0 <= M <= 6 * 104), the number of vertexes in the graph. The next N lines describes the initial
weight of each vertex (-106 <= weight[i] <= 106). The next part of each test case describes the edges in the graph at the beginning. Vertexes are numbered from 1 to N. The last part of each test case describes the operations to be performed on the
graph. It is guaranteed that the number of query operations [Q X K] in each case will be in the range [1, 2 * 105], and there will be no more than 2 * 105 operations that change the values of the vertexes [C X V].

There will be a blank line between two successive cases. A case with N = 0, M = 0 indicates the end of the input file and this case should not be processed by your program.

[align=left]Output[/align]
For each test case, output one real number – the average answer of all queries, in the format as indicated in the sample output. Please note that the result is rounded to six decimal places.

[align=left]Sample Input[/align]

3 3
10
20
30
1 2
2 3
1 3
D 3
Q 1 2
Q 2 1
D 2
Q 3 2
C 1 50
Q 1 1
E

3 3
10
20
20
1 2
2 3
1 3
Q 1 1
Q 1 2
Q 1 3
E

0 0


[align=left]Sample Output[/align]
Case 1: 25.000000
Case 2: 16.666667


题意:n个结点,m条边的无向图,每个节点有一个整数权值。

3种操作:

D X,删除id为X的边。输入保证每条边至多被删除一次。

Q X k , 计算与结点X联通的结点中(包括X),第K大的权值。如果不存在,输出0。

C X V,把结点X的权值改为V

输出所有询问的平均值。

题解:离线操作,我们把操作倒过来,把删边操作转换为加边。由于要询问第K大,所以我们用平衡树维护一个联通块中的点。当我们要合并两个联通块时,将结点数小的平衡树中的点加入另一个平衡树,删除结点数小的平衡树,这样的合并称为启发式合并。我们来分析一下复杂度,由于1个点合并以后,它所在的树的范围至少增大一倍。所以这个点最多合并logn次,每次合并的复杂度为logn,所以合并的总复杂度为O(n*logn*logn)。单次询问和修改的复杂度为logn。

代码如下:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<map>
#include<string.h>
#include<vector>
#define nn 21000
#define mod 100003
typedef long long LL;
typedef unsigned long long LLU;
const double eps=1e-8;
using namespace std;
int n,m;
int a[nn];
struct node
{
char c;
int x,k;
}ask[nn*30];
int u[nn*3],v[nn*3];
bool use[nn*3];
int fa[nn];
struct node1
{
int val;
int key;
int num;
int sum;
node1* son[2];
};
node1* Tree[nn];
void update(node1* id)
{
if(id==NULL)
return ;
id->sum=id->num;
if(id->son[0]!=NULL)
id->sum+=id->son[0]->sum;
if(id->son[1]!=NULL)
id->sum+=id->son[1]->sum;
}
void Rotate(node1* &id,int d)
{
node1* tem=id->son[d];
id->son[d]=tem->son[d^1];
tem->son[d^1]=id;
update(id);
update(tem);
id=tem;
}
void Insert(node1* &id,int val)
{
if(id==NULL)
{
id=new node1;
id->val=val;
id->key=rand()*rand();
id->num=id->sum=1;
id->son[0]=id->son[1]=NULL;
return ;
}
if(id->val==val)
{
id->num++;
id->sum++;
}
else if(val<id->val)
{
Insert(id->son[0],val);
update(id);
if(id->son[0]->key<id->key)
{
Rotate(id,0);
}
}
else
{
Insert(id->son[1],val);
update(id);
if(id->son[1]->key<id->key)
{
Rotate(id,1);
}
}
}
void Remove(node1* &id,int val)
{
if(id==NULL)
return ;
if(id->val==val)
{
if(id->num==1)
{
if(id->son[0]==NULL)
{
node1* tem=id;
id=id->son[1];
delete tem;
}
else if(id->son[1]==NULL)
{
node1* tem=id;
id=id->son[0];
delete tem;
}
else
{
if(id->son[0]->key<id->son[1]->key)
{
Rotate(id,0);
Remove(id->son[1],val);
}
else
{
Rotate(id,1);
Remove(id->son[0],val);
}
}
}
else
id->num--;
}
else if(id->val>val)
Remove(id->son[0],val);
else
Remove(id->son[1],val);
update(id);
}
int Kth(node1* id,int k)
{
if(id==NULL||k<=0||k>id->sum)
return 0;
int tem=0;
if(id->son[1]!=NULL)
tem+=id->son[1]->sum;
if(tem>=k)
{
return Kth(id->son[1],k);
}
else if(tem+id->num>=k)
{
return id->val;
}
else
return Kth(id->son[0],k-tem-id->num);
}
int findfa(int x)
{
if(fa[x]==x)
return x;
return fa[x]=findfa(fa[x]);
}
void Union(node1* &id,int y)
{
if(id==NULL)
return ;
Union(id->son[0],y);
Union(id->son[1],y);
for(int i=0;i<id->num;i++)
{
Insert(Tree[y],id->val);
}
delete id;
id=NULL;
}
void Clear(node1* &id)
{
if(id==NULL)
return ;
Clear(id->son[0]);
Clear(id->son[1]);
delete id;
id=NULL;
}
int main()
{
int i;
char s[5];
int cas=1;
while(scanf("%d%d",&n,&m)&&(n+m))
{
memset(use,false,sizeof(use));
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(i=1;i<=m;i++)
{
scanf("%d%d",&u[i],&v[i]);
}
int la=0;
while(1)
{
scanf("%s",s);
if(s[0]=='E')
break;
la++;
ask[la].c=s[0];
if(s[0]=='D')
{
scanf("%d",&ask[la].x);
use[ask[la].x]=true;
ask[la].k=-1;
}
else
{
scanf("%d%d",&ask[la].x,&ask[la].k);
if(s[0]=='C')
{
int tem=a[ask[la].x];
a[ask[la].x]=ask[la].k;
ask[la].k=tem;
}
}
}
for(i=1;i<=n;i++)
{
fa[i]=i;
Tree[i]=NULL;
Insert(Tree[i],a[i]);
}
for(i=1;i<=m;i++)
{
if(!use[i])
{
int fx=findfa(u[i]);
int fy=findfa(v[i]);
if(fx!=fy)
{
if(Tree[fx]->sum<Tree[fy]->sum)
{
fa[fx]=fy;
Union(Tree[fx],fy);
}
else
{
fa[fy]=fx;
Union(Tree[fy],fx);
}
}
}
}
double ans=0;
int q=0;
for(i=la;i>=1;i--)
{
if(ask[i].c=='D')
{
int fx=findfa(u[ask[i].x]);
int fy=findfa(v[ask[i].x]);
if(fx!=fy)
{
if(Tree[fx]->sum<Tree[fy]->sum)
{
fa[fx]=fy;
Union(Tree[fx],fy);
}
else
{
fa[fy]=fx;
Union(Tree[fy],fx);
}
}
}
else if(ask[i].c=='Q')
{
q++;
ans+=Kth(Tree[findfa(ask[i].x)],ask[i].k);
}
else
{
int ix=findfa(ask[i].x);
Remove(Tree[ix],a[ask[i].x]);
a[ask[i].x]=ask[i].k;
Insert(Tree[ix],ask[i].k);
}
}
for(i=1;i<=n;i++)
{
Clear(Tree[i]);
}
printf("Case %d: %.6lf\n",cas++,double(ans)/q);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: