Graph and Queries
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
The operations end with one single character, E, which indicates that the current case has ended. For simplicity, you only need to output one real number - the average answer of all queries.
There will be a blank line between two successive cases. A case with N = 0, M = 0 indicates the end of the input file and this case should not be processed by your program.
3 3 10 20 30 1 2 2 3 1 3 D 3 Q 1 2 Q 2 1 D 2 Q 3 2 C 1 50 Q 1 1 E 3 3 10 20 20 1 2 2 3 1 3 Q 1 1 Q 1 2 Q 1 3 E 0 0
【Sample Output】
Case 1: 25.000000 Case 2: 16.666667
【Hint】
For the first sample:
D 3 -- deletes the 3rd edge in the graph (the remaining edges are (1, 2) and (2, 3))
Q 1 2 -- finds the vertex with the second largest value among all vertexes connected with 1. The answer is 20.
Q 2 1 -- finds the vertex with the largest value among all vertexes connected with 2. The answer is 30.
D 2 -- deletes the 2nd edge in the graph (the only edge left after this operation is (1, 2))
Q 3 2 -- finds the vertex with the second largest value among all vertexes connected with 3. The answer is 0 (Undefined).
C 1 50 -- changes the value of vertex 1 to 50.
Q 1 1 -- finds the vertex with the largest value among all vertex connected with 1. The answer is 50.
E -- This is the end of the current test case. Four queries have been evaluated, and the answer to this case is (20 + 30 + 0 + 50) / 4 = 25.000.
For the second sample, caution about the vertex with same weight:
Q 1 1 – the answer is 20
Q 1 2 – the answer is 20
Q 1 3 – the answer is 10
【题意】
给出一张无向图,并在图中进行多种操作:
1.删除一条边;2.改变一个点的权值;3.询问x能够到达的所有点中,第k大的是多少
【分析】
花费了好多时间在这道题上,算是这段时间中做到的最综合的数据结构题了。
首先本题的无向图一开始就是个陷阱,如果单纯地从图的角度来考虑,每次询问都需要遍历全图来找第k大的值,这显然是不可取的,而中间又存在删边操作,图的连通性不是稳定的,结点的权值会变,而且可能多次改变,所以整个图是完全不稳定的,没办法用图论的方法来解决。
考虑倒过来操作,如果我们从做完所有操作之后最后的结果图出发,逆序回去,则原本的删边可以看成是将两个连通块连接到一起,询问第k值是在x点当前所属的连通块中进行,对点权的修改也是,而对于每一个独立的连通块,最后这两步可以用平衡树来实现。
所以算法的雏形就有了,询问连通块k值、修改连通块中点的点权操作——平衡树,维护点的连通性——并查集,保存点权的修改信息——前向星
完整过程:
1.首先从完整图出发,读入所有操作,记录删掉的边,按顺序记录点权的变化情况,记录其他信息;
2.用删边信息建立终图的连通性,并查集维护,对于每一个独立的连通块,建立一个独立的平衡树(这里我用的是SBT,网上题解搜出来好多人用的Splay,我其实有点不太理解,感觉这里没有需要用到Splay特殊结构的地方,单纯的维护平衡树的话Splay的稳定性和效率应该是不如SBT的。有大神路过看到这个的话,希望能交流下~~~);
3.从最后一条操作开始逆序回来:
i. 询问,则在x所属的平衡树中找第k值;
ii. 修改,则在x所属的平衡树中删掉原始的值,插入新值,这里对点权的顺序维护我用了前向星,要保证点权的操作也是要有序的;
iii.删边,在这里就是如果两个点属于两个不同的连通块,则将两个连通块连起来,并查集合并,同时平衡树合并。平衡树合并的时候只能把小的那棵树一个一个加到大的树中去,貌似Splay有个启发式合并,用了finger search神马的东西,可以把合并在O(nlogn)内完成,不会写,ORZ。
【后记】
写这道题的时候,SBT模板改了两次,-_-///,然后中间有SBT结合并查集结合前向星的,代码里面就是数组套数组套数组套数组......好多地方写着写着就写乱了,教训是如果能简单,一定不要往复杂了写。
然后并查集的教训:father[]绝对不能直接引用,必须调用getfather()
1 /* *********************************************** 2 MYID : Chen Fan 3 LANG : G++ 4 PROG : HDU3726 5 ************************************************ */ 6 7 #include <iostream> 8 #include <cstdio> 9 #include <cstring> 10 #include <algorithm> 11 12 using namespace std; 13 14 #define MAXN 20010 15 16 typedef struct enod 17 { 18 int p,q; 19 bool enable; 20 } enode; 21 enode e[60010]; 22 23 typedef struct qnod 24 { 25 int x,k; 26 } qnode; 27 qnode lisq[200010]; 28 29 typedef struct nod 30 { 31 int no,value,time; 32 } node; 33 node lis[300010]; 34 35 int sons[MAXN][2],size[MAXN],data[MAXN],sbt[MAXN],sbttail; 36 int lisd[60010],taild,tailq,tail,tailtot,lisc[200010],tailc=0; 37 int start[MAXN],num[MAXN],father[MAXN]; 38 char listot[500010]; 39 40 void rotate(int &t,int w) //rotate(&node,0/1) 41 { 42 int k=sons[t][1-w]; 43 if (!k) return ; 44 sons[t][1-w]=sons[k][w]; 45 sons[k][w]=t; 46 size[k]=size[t]; 47 size[t]=size[sons[t][0]]+size[sons[t][1]]+1; 48 t=k; 49 } 50 51 void maintain(int& t,bool flag) //maintain(&node,flag) 52 { 53 if (!t) return ; 54 if (!flag) 55 if (size[sons[sons[t][0]][0]]>size[sons[t][1]]) rotate(t,1); 56 else if (size[sons[sons[t][0]][1]]>size[sons[t][1]]) 57 { 58 rotate(sons[t][0],0); 59 rotate(t,1); 60 } else return ; 61 else 62 if (size[sons[sons[t][1]][1]]>size[sons[t][0]]) rotate(t,0); 63 else if (size[sons[sons[t][1]][0]]>size[sons[t][0]]) 64 { 65 rotate(sons[t][1],1); 66 rotate(t,0); 67 } else return ; 68 69 maintain(sons[t][0],false); 70 maintain(sons[t][1],true); 71 maintain(t,false); 72 maintain(t,true); 73 } 74 75 void insert(int& t,int v,int pos) //insert(&root,value) 76 { 77 if (!size[t]) 78 { 79 if (!pos) 80 { 81 sbttail++; 82 pos=sbttail; 83 } 84 data[pos]=v; 85 size[pos]=1; 86 sons[pos][0]=0; 87 sons[pos][1]=0; 88 t=pos; 89 } else 90 { 91 size[t]++; 92 if (v<data[t]) insert(sons[t][0],v,pos); 93 else insert(sons[t][1],v,pos); 94 maintain(t,v>=data[t]); 95 } 96 } 97 98 int last; 99 int del(int& t,int v) //node=del(&root,key) 100 { 101 size[t]--; 102 if (v==data[t]||(v<data[t]&&sons[t][0]==0)||(v>data[t]&&sons[t][1]==0)) 103 { 104 int ret=data[t]; 105 if (sons[t][0]==0||sons[t][1]==0) 106 { 107 last=t; 108 t=sons[t][1]+sons[t][0]; 109 } 110 else data[t]=del(sons[t][0],data[t]+1); 111 return ret; 112 } else 113 if (v<data[t]) return del(sons[t][0],v); 114 else return del(sons[t][1],v); 115 } 116 117 int select(int t,int k) 118 { 119 if (k==size[sons[t][0]]+1) return t; 120 if (k<=size[sons[t][0]]) return select(sons[t][0],k); 121 else return select(sons[t][1],k-1-size[sons[t][0]]); 122 } 123 124 void clean_father(int n) 125 { 126 for (int i=1;i<=n;i++) 127 { 128 father[i]=i; 129 sbt[i]=i; 130 } 131 } 132 133 int getfather(int x) 134 { 135 if (father[x]!=x) father[x]=getfather(father[x]); 136 return father[x]; 137 } 138 139 void link(int x,int y) 140 { 141 int xx=getfather(x),yy=getfather(y); 142 if (size[sbt[xx]]>size[sbt[yy]]) 143 { 144 father[yy]=xx; 145 while(size[sbt[yy]]>0) 146 { 147 int temp=del(sbt[yy],data[sbt[yy]]); 148 insert(sbt[xx],temp,last); 149 } 150 } 151 else 152 { 153 father[xx]=yy; 154 while(size[sbt[xx]]>0) 155 { 156 int temp=del(sbt[xx],data[sbt[xx]]); 157 insert(sbt[yy],temp,last); 158 } 159 } 160 } 161 162 bool op(node a,node b) 163 { 164 if (a.no==b.no) return a.time<b.time; 165 else return a.no<b.no; 166 } 167 168 int main() 169 { 170 freopen("3726.txt","r",stdin); 171 172 int n,m,tt=0; 173 while(scanf("%d%d",&n,&m)==2&&(n+m>0)) 174 { 175 for (int i=1;i<=n;i++) 176 { 177 lis[i].no=i; 178 lis[i].time=i; 179 scanf("%d",&lis[i].value); 180 } 181 for (int i=1;i<=m;i++) 182 { 183 scanf("%d%d",&e[i].p,&e[i].q); 184 e[i].enable=true; 185 } 186 taild=0;tailq=0;tailc=0;tail=n;tailtot=0; 187 bool doit=true; 188 while(doit) 189 { 190 char c=getchar(); 191 while(c!='D'&&c!='Q'&&c!='C'&&c!='E') c=getchar(); 192 tailtot++; 193 listot[tailtot]=c; 194 switch(c) 195 { 196 case 'D': 197 taild++; 198 scanf("%d",&lisd[taild]); 199 e[lisd[taild]].enable=false; 200 break; 201 case 'Q': 202 tailq++; 203 scanf("%d%d",&lisq[tailq].x,&lisq[tailq].k); 204 break; 205 case 'C': 206 tail++; 207 lis[tail].time=tail; 208 scanf("%d%d",&lis[tail].no,&lis[tail].value); 209 tailc++; 210 lisc[tailc]=lis[tail].no; 211 break; 212 default: 213 doit=false; 214 } 215 } 216 217 sort(&lis[1],&lis[tail+1],op); 218 int o=0; 219 memset(num,0,sizeof(num)); 220 for (int i=1;i<=tail;i++) 221 { 222 if (o!=lis[i].no) 223 { 224 o=lis[i].no; 225 start[o]=i; 226 } 227 num[o]++; 228 } 229 230 clean_father(n); 231 sbttail=0; 232 memset(size,0,sizeof(size)); 233 for (int i=1;i<=n;i++) insert(sbt[i],lis[start[i]+num[i]-1].value,0); 234 for (int i=1;i<=m;i++) 235 if (e[i].enable) 236 if (getfather(e[i].p)!=getfather(e[i].q)) 237 link(e[i].p,e[i].q); 238 239 int ansq=tailq; 240 double ans=0; 241 for (int i=tailtot-1;i>=1;i--) 242 switch(listot[i]) 243 { 244 case 'Q': 245 if (lisq[tailq].k>0&&size[sbt[getfather(lisq[tailq].x)]]>=lisq[tailq].k) 246 ans+=data[select(sbt[getfather(lisq[tailq].x)],size[sbt[getfather(lisq[tailq].x)]]-lisq[tailq].k+1)]; 247 tailq--; 248 break; 249 case 'D': 250 if (getfather(e[lisd[taild]].p)!=getfather(e[lisd[taild]].q)) 251 link(e[lisd[taild]].p,e[lisd[taild]].q); 252 taild--; 253 break; 254 case 'C': 255 num[lisc[tailc]]--; 256 del(sbt[getfather(lisc[tailc])],lis[start[lisc[tailc]]+num[lisc[tailc]]].value); 257 insert(sbt[getfather(lisc[tailc])],lis[start[lisc[tailc]]+num[lisc[tailc]]-1].value,last); 258 tailc--; 259 } 260 tt++; 261 printf("Case %d: %.6f ",tt,ans/ansq); 262 } 263 264 return 0; 265 }