题目链接: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 }