离线做法,逆序执行操作,那么原本的删除边的操作变为加入边的操作,用名次树维护每一个连通分量的名次,加边操作即是连通分量合并操作,每次将结点数小的子树向结点数大的子树合并,那么单次合并复杂度O(n1logn2),由于合并之后原本结点数少的子树结点数至少翻倍,所以每个结点最多被插入 logn 次,故总时间复杂度为
O(n log2n) 。
注意细节处理,代码如下:
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 7 using namespace std; 8 9 10 struct Node { 11 Node *ch[2]; 12 int r; 13 int v; 14 int s; 15 Node(int vv): v(vv) { 16 s = 1; 17 ch[0] = ch[1] = NULL; 18 r = rand(); 19 } 20 int cmp(int x) const { 21 if(x == v) return -1; 22 return x < v ? 0 : 1; 23 } 24 void maintain() { 25 s = 1; 26 if(ch[0] != NULL) s += ch[0]->s; 27 if(ch[1] != NULL) s += ch[1]->s; 28 } 29 }; 30 31 void rotate(Node* &o, int d) { 32 Node* k = o->ch[d^1]; o->ch[d^1] = k->ch[d]; k->ch[d] = o; 33 o->maintain(); k->maintain(); o = k; 34 } 35 36 void insert(Node* &o, int x) { 37 if(o == NULL) o = new Node(x); 38 else { 39 int d = x < o->v ? 0 : 1; 40 insert(o->ch[d], x); 41 if(o->ch[d]->r > o->r) rotate(o, d^1); 42 } 43 o->maintain(); 44 } 45 void remove(Node* &o, int x) { 46 int d = o->cmp(x); 47 Node* u = o; 48 if(d == -1) { 49 if(o->ch[0] != NULL && o->ch[1] != NULL){ 50 int d2 = o->ch[0]->r > o->ch[1]->r ? 1 : 0; 51 rotate(o, d2); 52 remove(o->ch[d2], x); 53 } 54 else { 55 if(o->ch[0] == NULL) o = o->ch[1]; else o = o->ch[0]; 56 delete u; 57 } 58 } 59 else remove(o->ch[d], x); 60 if(o != NULL) o->maintain(); 61 } 62 63 int kth(Node* o, int k) { 64 if(o == NULL || k > o->s || k <= 0) return 0; 65 int s = o->ch[1] == NULL ? 0 : o->ch[1]->s; 66 if(k == s+1) return o->v; 67 else if(k <= s) return kth(o->ch[1], k); 68 else return kth(o->ch[0], k-s-1); 69 } 70 71 72 struct cmd { 73 char type; 74 int x, p; 75 }; 76 77 vector<cmd> cmds; 78 79 const int maxn = 2e4 + 10; 80 const int maxm = 6e4 + 10; 81 int n, m; 82 int weight[maxn], from[maxm], to[maxm], removed[maxm]; 83 84 int pa[maxn]; 85 int findpa(int x) {return x == pa[x] ? x : pa[x] = findpa(pa[x]);} 86 long long sum; 87 int cnt; 88 Node* root[maxn]; 89 90 void mergetreeto(Node* &ser, Node* &to) { 91 if(ser->ch[0] != NULL) mergetreeto(ser->ch[0], to); 92 if(ser->ch[1] != NULL) mergetreeto(ser->ch[1], to); 93 insert(to, ser->v); 94 delete ser; 95 ser = NULL; 96 } 97 98 void removetree(Node *&ser) { 99 if(ser == NULL) return; 100 if(ser->ch[0] != NULL) removetree(ser->ch[0]); 101 if(ser->ch[1] != NULL) removetree(ser->ch[1]); 102 delete ser; 103 ser = NULL; 104 } 105 106 void add_edge(int id) { 107 int x = findpa(pa[from[id]]); 108 int y = findpa(pa[to[id]]); 109 if(x != y) { 110 if(root[x]->s < root[y]->s) mergetreeto(root[x], root[y]), pa[x] = y; 111 else mergetreeto(root[y], root[x]), pa[y] = x; 112 } 113 } 114 115 void querycnt(int x, int k) { 116 cnt++; 117 sum += kth(root[findpa(x)], k); 118 } 119 120 void change_w(int x, int v) { 121 int u = findpa(pa[x]); 122 remove(root[u], weight[x]); 123 insert(root[u], v); 124 weight[x] = v; 125 } 126 127 void init() { 128 cmds.clear(); 129 cnt = 0; 130 sum = 0; 131 memset(removed, 0, sizeof removed); 132 for(int i = 1; i < n; i++) removetree(root[i]); 133 } 134 int main() { 135 int kase = 0; 136 while(scanf("%d%d", &n, &m) == 2 && n) { 137 for(int i = 1; i <= n; i++) scanf("%d", &weight[i]); 138 for(int i = 1; i <= m; i++) { 139 int u, v; 140 scanf("%d%d", &u, &v); 141 from[i] = u; 142 to[i] = v; 143 } 144 init(); 145 while(1) { 146 getchar(); 147 char ch; 148 scanf("%c", &ch); 149 cmd C; 150 C.type = ch; 151 C.x = C.p = 0; 152 if(ch == 'E') break; 153 scanf("%d", &C.x); 154 if(ch == 'D') removed[C.x] = 1; 155 if(ch == 'Q') scanf("%d", &C.p); 156 if(ch == 'C') { 157 scanf("%d", &C.p); 158 swap(C.p, weight[C.x]); 159 } 160 cmds.push_back(C); 161 } 162 for(int i = 1; i <= n; i++) { 163 pa[i] = i; 164 root[i] = new Node(weight[i]); 165 } 166 for(int i = 1; i <= m; i++) 167 if(!removed[i]) add_edge(i); 168 169 for(int i = cmds.size()-1; i >= 0; i--) { 170 cmd C = cmds[i]; 171 if(C.type == 'D') add_edge(C.x); 172 if(C.type == 'C') change_w(C.x, C.p); 173 if(C.type == 'Q') querycnt(C.x, C.p); 174 } 175 printf("Case %d: %.6lf\n", ++kase, sum/double(cnt)); 176 } 177 return 0; 178 }