UVA 1479 Graph and Queries(Treap:名次树+并查集)
题意:
给你一个无向图,每个顶点具有权值,现在对图进行操作,你需要输出最后查询操作的平均值.有查询,删除边,改变点的权值三种操作.
分析:
刘汝佳训练指南P234例题.
首先读入所有的操作,点以及边.由于只需要我们输出最后的一个平均结果,所以我们把这些操作逆序处理,使得从图中删除边,变成往图中添加边并合并连通分量.
首先读入所有命令,对于删除边i的命令,我们就令边i无效即可.(我们依然保存所有边的信息).对于修改x点权值为v的命令,我们就直接改变x的权值y为v,并且保存一条该x权值为y的命令.(即逆转)对于查询命令,我们不管直接保存下来.
现在我们对初始的每个节点建立一个单独的Treap节点,并且一一读入所有有效的边(即经过所有删除操作还剩下的边)并且合并两个边所属的连通分量(同时合并两个连通分量的Treap树,将小树的节点一一合入大树).
接下来我们就逆序处理所有的命令了,对于D命令就是添加边合并连通分量并且合并Treap树.对于Q命令就是查询当前节点所属连通分量Treap树的第K大值V.对于C命令就是先删除特定节点在插入特定节点.
总之对于一个节点x,任何时候它所属的Treap数根编号都是它的连通分量的根的编号.
代码少写了merge中b的应用符号,WA了半天.
AC代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
struct Node
{
Node *ch[2];
int r,v,s;//s表示节点数
Node(int v):v(v)
{
ch[0]=ch[1]=NULL;
r=rand();
s=1;
}
bool operator<(const Node &b)const
{
return r<b.r;
}
int cmp(int x)
{
if(x==v)return -1;
return x<v?0:1;
}
void maintain()
{
s=1;
if(ch[0]!=NULL) s+=ch[0]->s;
if(ch[1]!=NULL) s+=ch[1]->s;
}
};
void rotate(Node* &o,int d)
{
Node *k=o->ch[d^1];
o->ch[d^1]=k->ch[d];
k->ch[d]=o;
o->maintain();
k->maintain();
o=k;
}
void insert(Node* &o,int x)//o子树中事先不存在x
{
if(o==NULL)
{
o=new Node(x);
}
else
{
int d=(x < o->v)? 0:1;//允许相同的v值在Treap中,所以注意这里的写法,不能用cmp
insert(o->ch[d],x);
if(o->ch[d]>o)
rotate(o,d^1);
}
o->maintain();
}
void remove(Node* &o,int x)//o子树中实现必须存在x
{
int d=o->cmp(x);
if(d==-1)
{
Node *u=o;
if(o->ch[0] && o->ch[1])
{
int d2=(o->ch[0]> o->ch[1])?1:0;
rotate(o,d2);
remove(o->ch[d2],x);
}
else
{
if(o->ch[0]==NULL) o=o->ch[1];
else o=o->ch[0];
delete u;
}
}
else remove(o->ch[d],x);
if(o) o->maintain();//之前o存在,但是删除节点后o可能就是空NULL了,所以需要先判断o是否为空
}
const int maxn=20000+100;
const int maxm=60000+100;
int weight[maxn];
struct edge
{
int u,v;
}edges[maxm];
struct command
{
int type;//0,1,2,对于D,Q,C
int x,p;
}coms[600000+1000];
int cnt;//命令条数
bool removed[maxm];
int F[maxn];
int findset(int i)
{
if(F[i]==-1) return i;
return F[i]=findset(F[i]);
}
Node *nodes[maxn];
int kth(Node* o,int k)//注意这是求第k大,不是第k小.
{
if(o==NULL || k<=0 || k> o->s) return 0;
int s=(o->ch[1]==NULL)?0:o->ch[1]->s;
if(k==s+1) return o->v;
else if(k<=s) return kth(o->ch[1],k);
else return kth(o->ch[0],k-s-1);
}
void merge(Node *&a,Node *&b)//少写了b的应用符号,WA了半天
{
if(a->ch[0]) merge(a->ch[0],b);
if(a->ch[1]) merge(a->ch[1],b);
insert(b,a->v);
delete a;
a=NULL;
}
void removetree(Node *&a)
{
if(a->ch[0]) removetree(a->ch[0]);
if(a->ch[1]) removetree(a->ch[1]);
delete a;
a=NULL;
}
void add_edge(int e)
{
int x=findset(edges[e].u), y=findset(edges[e].v);
if(x!=y)
{
if(nodes[x]->s < nodes[y]->s) {F[x]=y; merge(nodes[x],nodes[y]);}
else {F[y]=x; merge(nodes[y],nodes[x]);}
}
}
int query_cnt;
long long query_tot;
void query(int x,int k)
{
int fx=findset(x);
query_tot+= kth(nodes[fx],k);
query_cnt++;
}
void change_weight(int x,int v)
{
int u=findset(x);
remove(nodes[u],weight[x]);
insert(nodes[u],v);
weight[x]=v;
}
int main()
{
int n,m,kase=0;
while(scanf("%d%d",&n,&m)==2&&n)
{
if(n==0&&m==0) break;
memset(removed,0,sizeof(removed));
for(int i=1;i<=n;i++)
scanf("%d",&weight[i]);
for(int i=1;i<=m;i++)
scanf("%d%d",&edges[i].u,&edges[i].v);
char str[100];
int x,p;
cnt=0;
while(scanf("%s",str)==1)
{
if(str[0]=='E') break;
scanf("%d",&x);
if(str[0]=='D')
{
coms[cnt++]=(command){0,x,0};
removed[x]=true;
}
else if(str[0]=='Q')
{
scanf("%d",&p);
coms[cnt++]=(command){1,x,p};
}
else if(str[0]=='C')
{
int v;
scanf("%d",&v);
p=weight[x];
weight[x]=v;
coms[cnt++]=(command){2,x,p};
}
}
for(int i=1;i<=n;i++)
{
F[i]=-1;
if(nodes[i]) removetree(nodes[i]);
nodes[i]=new Node(weight[i]);
}
for(int i=1;i<=m;i++)if(!removed[i])
add_edge(i);
query_tot=query_cnt=0;
for(int i=cnt-1;i>=0;i--)
{
if(coms[i].type==0) add_edge(coms[i].x);
else if(coms[i].type==1) query(coms[i].x,coms[i].p);
else if(coms[i].type==2) change_weight(coms[i].x,coms[i].p);
}
//printf("tot=%I64d, cnt=%d\n",query_tot,query_cnt);
printf("Case %d: %.6lf\n", ++kase, query_tot / (double)query_cnt);
}
return 0;
}