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; }