题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20332
【思路】
时光倒流+名次树(rank tree)。
所谓“时光倒流”即逆向处理,因为D删除边并不好操作所以我们倒着处理,删除边转化为添加边,C转化为将weight变回操作前的数,Q不变。
名次树实现以上操作:名次树是Treap+s域实现的,可以提供kth即查询第k大的数的操作和Treap的所有功能。
1)对于D(x):合并from[x]与to[x]所在的rank tree,后序思想,将src中的结点逐个添加到dest中,采用启发式合并。
2)对于Q(x,k):调用kth操作同时累计cnt与tot。
3)对于C(x,v):调用一次romove(root[findset(x)],weight[x]),再调用一次insert(root[findset(x)],v)。
用到一个并查集快速寻找x所属rank tree的根。
【代码】
1 #include<cstdio> 2 #include<ctime> 3 #include<cstring> 4 #include<cstdlib> 5 #include<vector> 6 #include<iostream> 7 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 8 using namespace std; 9 10 const int maxn = 120000+10; 11 //Treap相关 12 struct Node{ 13 Node *ch[2]; 14 int r,v,s; //r为优先级 v为键值 s为结点总数 15 Node(int w) :v(w) { ch[0]=ch[1]=NULL; s=1; r=rand(); } 16 int cmp(int x) const{ //x应在左子树d=0 x应在右子树d=1 17 if(x==v) return -1; 18 return x<v? 0:1; 19 } 20 int cmp2(int x) const{ return x<v? 0:1; } 21 void maintain() { //名次树维护 s 22 s=1; 23 if(ch[0]!=NULL) s+=ch[0]->s; 24 if(ch[1]!=NULL) s+=ch[1]->s; 25 } 26 }; 27 void rotate(Node* &o,int d) { //旋转操作 d=0左旋d=1右旋 28 Node* k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o; 29 o->maintain();k->maintain(); o=k; 30 } 31 void insert(Node* &o,int x) { 32 if(o==NULL) o=new Node(x); 33 else { 34 int d=o->cmp2(x); //可能会有键值相等的结点 35 insert(o->ch[d],x); 36 if(o->ch[d]->r > o->r) rotate(o,d^1); //如果插入之后不满足堆的性质则反方向旋转 37 } 38 o->maintain(); //维护 39 } 40 void remove(Node* &o,int x) { 41 int d=o->cmp(x); 42 if(d==-1) { 43 Node *u=o; 44 if(o->ch[0]!=NULL && o->ch[1]!=NULL){ //根据左右子[优先级]旋转 旋转后递归删除x 45 int d2=o->ch[0]->r > o->ch[1]->r? 1:0; 46 rotate(o,d2); remove(o->ch[d2],x); 47 } 48 else { 49 if(o->ch[0]!=NULL) o=o->ch[0]; else o=o->ch[1]; 50 delete u; 51 } 52 } 53 else 54 remove(o->ch[d],x); 55 if(o!=NULL) o->maintain(); 56 } 57 //名次树相关 58 int kth(Node* o,int k) { //返回第k[大]的数 59 if(o==NULL || k<=0 || k>o->s) return 0; 60 int s=o->ch[1]==NULL? 0:o->ch[1]->s; 61 if(k==s+1) return o->v; 62 else if(k<=s) return kth(o->ch[1],k); 63 else return kth(o->ch[0],k-s-1); 64 } 65 void mergeto(Node* &src,Node* &dest) { //[后序遍历]合并两棵名次树 66 if(src->ch[0]!=NULL) mergeto(src->ch[0],dest); 67 if(src->ch[1]!=NULL) mergeto(src->ch[1],dest); 68 insert(dest,src->v); 69 delete src; 70 src=NULL; 71 } 72 void removetree(Node* &o) { //[后序遍历]删除一棵名次树 73 if(o->ch[0]!=NULL) removetree(o->ch[0]); 74 if(o->ch[1]!=NULL) removetree(o->ch[1]); 75 delete o; 76 o=NULL; 77 } 78 //并查集相关 79 int pa[maxn]; 80 int findset(int u) { return u==pa[u]? u:pa[u]=findset(pa[u]); } 81 //题目相关 82 Node* root[maxn]; 83 int weight[maxn],from[maxn],to[maxn]; 84 bool removed[maxn]; 85 int n,m,cnt,kase; 86 long long sum; 87 struct Command{ char type; int a,b; 88 }; 89 vector<Command> cs; 90 91 void addedge(int i) { 92 int u=findset(from[i]),v=findset(to[i]); 93 if(u!=v) { 94 if(root[u]->s > root[v]->s) { pa[v]=u; mergeto(root[v],root[u]); } 95 else { pa[u]=v; mergeto(root[u],root[v]); } 96 } 97 } 98 void query(int x,int k) { 99 cnt++; 100 sum+=kth(root[findset(x)],k); 101 } 102 void changeWeight(int x,int p) { 103 int u=findset(x); 104 remove(root[u],weight[x]); 105 insert(root[u],p); 106 weight[x]=p; 107 } 108 109 int main() { 110 srand(time(0)); //初始化随机数种子 111 kase=0; 112 while(scanf("%d%d",&n,&m)==2 && (n&&m)) { 113 FOR(i,1,n) scanf("%d",&weight[i]); 114 FOR(i,1,m) scanf("%d%d",&from[i],&to[i]); 115 char type; 116 memset(removed,0,sizeof(removed)); 117 cs.clear(); 118 while(scanf(" %c",&type)==1 && type!='E') { 119 int x=0,p=0; 120 scanf("%d",&x); 121 if(type=='D') removed[x]=1; 122 if(type=='Q') scanf("%d",&p); 123 if(type=='C') { 124 scanf("%d",&p); 125 swap(p,weight[x]); 126 } 127 cs.push_back((Command){type,x,p}); 128 } 129 FOR(i,1,n) { 130 pa[i]=i; if(root[i]!=NULL) removetree(root[i]); 131 root[i]=new Node(weight[i]); 132 } 133 FOR(i,1,m) if(!removed[i]) addedge(i); 134 int d=cs.size(); sum=cnt=0; 135 for(int i=d-1;i>=0;i--) { 136 if(cs[i].type=='D') addedge(cs[i].a); 137 if(cs[i].type=='Q') query(cs[i].a,cs[i].b); 138 if(cs[i].type=='C') changeWeight(cs[i].a,cs[i].b); 139 } 140 printf("Case %d: %.6lf\n",++kase,sum/(double)cnt); 141 } 142 return 0; 143 }