题目链接:https://vjudge.net/contest/159527#problem/A
题意:(求一个 图 中的连通分量中的 第 k 大)
一张图,n 个点,m 条边,
有一些操作:
删除 ID 为 x 的边,(从 1 到 m);
询问 x 所在的连通分量 里面第 k 大的权值;
把结点 X 的权值 改成 V;
求:
所有的询问后,计算平均值;
每个连通分量都是一颗Treap树,加边操作,就是树的合并;
刘汝佳采用的是离线算法,我还是第一次听说,但是还是可以按照题意直接模拟的(我猜,但是很麻烦);因为在Treap中删边不同于删点;
什么是离线算法呢?
把操作顺序反过来处理,执行完所有 删除边操作,然后建Treap,要是不在同一个连通分量里面(并查集判断),这就涉及到递归合并Treap树了,这里采用了启发式合并;
然后反向操作,遇到 D,就是加边(加边操作同上),
询问,就是在 X 所在连通分量里面,寻找第 k 大;
改权,就是删除这个点,然后从新加点;
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 struct Node 6 { 7 Node *ch[2]; 8 int r; //优先级 9 int v; //值 10 int s; //结点总数 11 12 Node(int v):v(v) 13 { 14 ch[0] = ch[1] = NULL; 15 r = rand(); 16 s = 1; 17 } 18 19 bool operator < (const Node& rhs) const 20 { 21 return r < rhs.r; 22 } 23 24 int cmp(int x) const 25 { 26 if(x==v) return -1; 27 return x < v ? 0 : 1; 28 } 29 30 void maintain() 31 { 32 s = 1; 33 if(ch[0]!=NULL) s+=ch[0]->s; 34 if(ch[1]!=NULL) s+=ch[1]->s; 35 } 36 37 38 }; 39 40 void rotate(Node* &o,int d) 41 { 42 Node* k = o->ch[d^1]; 43 o->ch[d^1] = k ->ch[d]; 44 k->ch[d] = o; 45 o->maintain(); 46 k->maintain(); 47 o = k; 48 } 49 50 void insert(Node* &o,int x) 51 { 52 if(o==NULL) o = new Node(x); 53 else 54 { 55 int d = (x < o->v? 0 : 1); 56 insert(o->ch[d],x); 57 if(o->ch[d]->r > o->r) 58 rotate(o,d^1); 59 } 60 o->maintain(); 61 } 62 63 void remove(Node* &o,int x) 64 { 65 int d = o->cmp(x); 66 if(d==-1) 67 { 68 Node* u = 0; 69 if(o->ch[0]!=NULL&&o->ch[1]!=NULL) 70 { 71 int d2 = (o->ch[0]->r > o->ch[1]->r ? 1 : 0); 72 rotate(o,d2); 73 remove(o->ch[d2],x); 74 } 75 else 76 { 77 if(o->ch[0]==NULL) 78 o = o->ch[1]; 79 else o = o->ch[0]; 80 } 81 } 82 else 83 remove(o->ch[d],x); 84 85 if(o!=NULL) o->maintain(); 86 } 87 88 89 const int maxc = 500000 + 10; 90 struct Command 91 { 92 char type; 93 int x,p; 94 } commands[maxc]; 95 96 const int maxn = 20000 + 10; 97 const int maxm = 60000 + 10; 98 int n,m; 99 int weight[maxn],from[maxm],to[maxm],removed[maxm]; 100 101 102 int pa[maxn]; 103 int findset(int x) 104 { 105 return pa[x]!=x ? pa[x] = findset(pa[x]):x; 106 } 107 108 Node* root[maxn]; //Treap 109 110 int kth(Node* o,int k) 111 { 112 if(o==NULL||k<=0||k> o->s) return 0; 113 int s = (o->ch[1]==NULL?0:o->ch[1]->s); 114 if(k==s+1) return o->v; 115 else if(k<=s) return kth(o->ch[1],k); 116 else return kth(o->ch[0],k-s-1); 117 } 118 119 void mergeto(Node* &src,Node* &dest) 120 { 121 if(src->ch[0]!=NULL) mergeto(src->ch[0],dest); 122 if(src->ch[1]!=NULL) mergeto(src->ch[1],dest); 123 insert(dest,src->v); 124 delete src; 125 src = NULL; 126 } 127 128 void removetree(Node* &x) 129 { 130 if(x->ch[0]!=NULL) removetree(x->ch[0]); 131 if(x->ch[1]!=NULL) removetree(x->ch[1]); 132 delete x; 133 x = NULL; 134 } 135 136 void add_edge(int x) 137 { 138 int u = findset(from[x]),v=findset(to[x]); 139 if(u!=v) 140 { 141 if(root[u]->s < root[v]->s) 142 { 143 pa[u] = v; 144 mergeto(root[u],root[v]); 145 } 146 else 147 { 148 pa[v] = u; 149 mergeto(root[v],root[u]); 150 } 151 } 152 } 153 154 int query_cnt; 155 long long query_tot; 156 void query(int x,int k) 157 { 158 query_cnt++; 159 query_tot +=kth(root[findset(x)],k); 160 } 161 162 void change_weight(int x,int v) 163 { 164 int u = findset(x); 165 remove(root[u],weight[x]); 166 insert(root[u],v); 167 weight[x] = v; 168 } 169 170 171 int main() 172 { 173 int kase = 0; 174 while(scanf("%d%d",&n,&m)==2&&n) 175 { 176 for(int i=1; i<=n; i++) 177 scanf("%d",&weight[i]); 178 for(int i=1; i<=m; i++) 179 scanf("%d%d",&from[i],&to[i]); 180 memset(removed,0,sizeof(removed)); 181 182 int c = 0; 183 for(;;) 184 { 185 char type; 186 int x,p=0,v = 0; 187 scanf(" %c",&type); 188 if(type=='E') break; 189 scanf("%d",&x); 190 if(type=='D') removed[x]= 1; //删掉的边 191 if(type=='Q') scanf("%d",&p); 192 if(type=='C') 193 { 194 scanf("%d",&v); 195 p = weight[x]; 196 weight[x] = v; 197 } 198 commands[c++] = (Command) 199 { 200 type,x,p 201 }; 202 } 203 204 //最终的图 205 for(int i=1; i<=n; i++) 206 { 207 pa[i] = i; 208 if(root[i]!=NULL) removetree(root[i]); 209 root[i] = new Node(weight[i]); 210 } 211 for(int i=1; i<=m; i++) 212 { 213 if(!removed[i]) //id为i这条边没有被删掉 214 add_edge(i); 215 } 216 217 query_cnt = query_tot = 0; 218 for(int i=c-1; i>=0; i--) 219 { 220 if(commands[i].type=='D') add_edge(commands[i].x); //加上边 221 if(commands[i].type=='Q') query(commands[i].x,commands[i].p);//第p大 222 if(commands[i].type=='C') change_weight(commands[i].x,commands[i].p); 223 } 224 225 printf("Case %d: %.6lf\n",++kase,query_tot/(double)query_cnt); 226 227 } 228 229 230 231 return 0; 232 }