【BZOJ 1146】网络管理Network
Description
M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门。为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络。该网络的结构由N个路由器和N-1条高速光缆组成。每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络。该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信。 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略。但是由于路由器老化,在这些路由器上进行数据交换会带来很大的延迟。而两个路由器之间的通信延迟时间则与这两个路由器通信路径上所有路由器中最大的交换延迟时间有关。作为M公司网络部门的一名实习员工,现在要求你编写一个简单的程序来监视公司的网络状况。该程序能够随时更新网络状况的变化信息(路由器数据交换延迟时间的变化),并且根据询问给出两个路由器通信路径上延迟第k大的路由器的延迟时间。【任务】 你的程序从输入文件中读入N个路由器和N-1条光缆的连接信息,每个路由器初始的数据交换延迟时间Ti,以及Q条询问(或状态改变)的信息。并依次处理这Q条询问信息,它们可能是: 1. 由于更新了设备,或者设备出现新的故障,使得某个路由器的数据交换延迟时间发生了变化。 2. 查询某两个路由器a和b之间的路径上延迟第k大的路由器的延迟时间。
Input
第一行为两个整数N和Q,分别表示路由器总数和询问的总数。第二行有N个整数,第i个数表示编号为i的路由器初始的数据延迟时间Ti。紧接着N-1行,每行包含两个整数x和y。表示有一条光缆连接路由器x和路由器y。紧接着是Q行,每行三个整数k、a、b。如果k=0,则表示路由器a的状态发生了变化,它的数据交换延迟时间由Ta变为b。如果k>0,则表示询问a到b的路径上所经过的所有路由器(包括a和b)中延迟第k大的路由器的延迟时间。注意a可以等于b,此时路径上只有一个路由器。
Output
对于每一个第二种询问(k>0),输出一行。包含一个整数为相应的延迟时间。如果路径上的路由器不足k个,则输出信息“invalid request!”(全部小写不包含引号,两个单词之间有一个空格)。
Sample Input
5 1 2 3 4
3 1
2 1
4 3
5 3
2 4 5
0 1 2
2 2 3
2 1 4
3 3 5
Sample Output
2
2
invalid request!
HINT
100% 测试数据满足N,Q<=80000,任意一个路由器在任何时刻都满足延迟时间小于10^8。对于所有询问满足0<=K<=N 。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 #define rint register int 6 7 int n, q, u, v, pow[100000]; 8 int k[100000], a[100000], b[100000]; 9 int begin[100000], end[100000]; 10 int fa[100000][22], depth[100000]; 11 12 char ch,B[1<<15],*S=B,*T=B,buf[1<<21],*O=buf,stk[40]; 13 #define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) 14 inline const int getint() //输入优化 15 { 16 char c = getc(); 17 rint k = 1, r = 0; 18 for(; c < '0' || c > '9'; c = getc()) 19 if(c == '-') k = -1; 20 for(; c >= '0' && c <= '9'; c = getc()) 21 r = r * 10 + c - '0'; 22 return k * r; 23 } 24 25 int lca(rint x, rint y) //找LCA 26 { 27 rint i; 28 if (depth[x] < depth[y]) 29 x ^= y, y ^= x, x ^= y; 30 for (i = fa[x][21]; depth[x] > depth[y]; i--) 31 if (depth[fa[x][i]] >= depth[y]) x = fa[x][i]; 32 if (x == y) return x; 33 for (i = fa[x][21]; i >= 0; i--) 34 if (fa[x][i] != fa[y][i]) 35 x = fa[x][i], y = fa[y][i]; 36 return fa[x][0]; 37 38 } 39 40 /*********edges**********/ 41 int ep[200000], et[200000]; 42 int last[100000], en; 43 44 inline void addedge(rint f, rint t) //邻接表 45 { 46 en++; 47 ep[en] = last[f]; 48 last[f] = en; 49 et[en] = t; 50 } 51 52 /*****Discretization*****/ 53 int num[200000], count; 54 55 int lb(rint key) // lower_bound 56 { 57 rint l = 1, r = count, mid; 58 while (l < r) 59 { 60 mid = l + r >> 1; 61 if (num[mid] == key) 62 return mid; 63 if (num[mid] < key) 64 l = mid + 1; 65 else r = mid - 1; 66 } 67 return l; 68 } 69 70 void discretizate() //离散化 71 { 72 std::sort(num + 1, num + count + 1); 73 rint tmp = 0, i; 74 num[++count] = 1000000013; 75 for (i = 1; i <= count; i++) 76 { 77 if (num[i] != num[i + 1]) 78 num[++tmp] = num[i]; 79 } 80 count = tmp; 81 for (i = 1; i <= n; i++) 82 { 83 pow[i] = lb(pow[i]); 84 } 85 } 86 87 /********ZX-Tree*********/ 88 int nls[10000000], nrs[10000000], nsum[10000000]; 89 90 int ns, tmpos, tmdata; 91 92 void tmodify(int &t, rint left, rint right) //主席树上修改 93 { 94 if (t == 0) 95 { 96 nsum[t = ++ns] = 0; 97 } 98 if (left < right) 99 { 100 int mid = left + right >> 1; 101 if (tmpos <= mid) tmodify(nls[t], left, mid); 102 else tmodify(nrs[t], mid + 1, right); 103 nsum[t] += tmdata; 104 } else nsum[t] += tmdata; 105 } 106 107 /*******tree-array*******/ 108 int root[100000], rts; 109 110 void modify(rint x, rint y, rint pos, rint data) //树状数组上修改一段区间 111 { 112 tmpos = pos, tmdata = data; 113 for (; x <= n; x += x & -x) 114 tmodify(root[x], 1, count); 115 tmdata = -data; 116 for (y++; y <= n; y += y & -y) 117 tmodify(root[y], 1, count); 118 } 119 120 int rs[4][30]; 121 122 int init_rs(rint i, rint x) //用rs数组记录查询时需要用到的点 123 { 124 memset(rs[i], 0, sizeof (rs[i])); 125 int ret = 0; 126 for (; x > 0; x -= x & -x) 127 { 128 rs[i][++rs[i][0]] = root[x]; 129 ret += nsum[root[x]]; 130 } 131 return ret; 132 } 133 134 int query_rs() //查询每个点右儿子的大小 135 { 136 int ret = 0; rint i; 137 for (i = 1; i <= rs[0][0]; i++) 138 ret += nsum[nrs[rs[0][i]]]; 139 for (i = 1; i <= rs[1][0]; i++) 140 ret += nsum[nrs[rs[1][i]]]; 141 for (i = 1; i <= rs[2][0]; i++) 142 ret -= nsum[nrs[rs[2][i]]]; 143 for (i = 1; i <= rs[3][0]; i++) 144 ret -= nsum[nrs[rs[3][i]]]; 145 return ret; 146 } 147 148 void down(rint d) //将每个点变成它的左/右儿子 149 { 150 rint x, i; 151 for (x = 0; x < 4; x++) 152 for (i = 1; i <= rs[x][0]; i++) 153 rs[x][i] = d ? nrs[rs[x][i]] : nls[rs[x][i]]; 154 } 155 156 int query(rint x, rint y, rint data) //查询 157 { 158 int s = 0, f = lca(x, y), mid; 159 s += init_rs(0, begin[x]); 160 s += init_rs(1, begin[y]); 161 s -= init_rs(2, begin[f]); 162 s -= init_rs(3, begin[fa[f][0]]); 163 if (s < data) return -1; 164 int left = 1, right = count; 165 while (left < right) //迭代 166 { 167 mid = left + right >> 1; 168 s = query_rs(); 169 if (data > s) 170 { 171 down(0); 172 right = mid; 173 data -= s; 174 } 175 else 176 { 177 down(1); 178 left = mid + 1; 179 } 180 } 181 return num[left]; 182 } 183 184 /**********dfs***********/ 185 void DFS(rint x) //找出dfs序和求lca的rmq数组 186 { 187 rint i; 188 depth[x] = depth[fa[x][0]] + 1; 189 for (i = 0; fa[x][i]; fa[x][21] = ++i) 190 fa[x][i + 1] = fa[fa[x][i]][i]; 191 begin[x] = ++rts; 192 root[begin[x]] = ++ns; 193 for (i = last[x]; i; i = ep[i]) 194 if (begin[et[i]] == 0) 195 fa[et[i]][0] = x, DFS(et[i]); 196 end[x] = rts; 197 } 198 199 int main() 200 { 201 n = getint(); 202 q = getint(); 203 count = 0; 204 rint i, tmp; 205 for (i = 1; i <= n; i++) 206 num[++count] = pow[i] = getint(); 207 for (i = 1; i < n; i++) 208 { 209 u = getint(); 210 v = getint(); 211 addedge(u, v); 212 addedge(v, u); 213 } 214 for (i = 1; i <= q; i++) 215 { 216 k[i] = getint(); 217 a[i] = getint(); 218 b[i] = getint(); 219 if (k[i] == 0) num[++count] = b[i]; 220 } 221 discretizate(); 222 DFS(1); 223 for (i = 1; i <= n; i++) 224 { 225 modify(begin[i], end[i], pow[i], 1); 226 } 227 for (i = 1; i <= q; i++) 228 { 229 if (k[i]) 230 { 231 tmp = query(a[i], b[i], k[i]); 232 if (tmp >= 0) printf("%d\n", tmp); 233 else printf("invalid request!\n"); 234 } 235 else 236 { 237 modify(begin[a[i]], end[a[i]], pow[a[i]], -1); 238 pow[a[i]] = lb(b[i]); 239 modify(begin[a[i]], end[a[i]], pow[a[i]], 1); 240 } 241 } 242 }
改进:
发现还是不够快,研究了一下其它博客上飞快的代码,才知道原来他们把原本的内容和修改的内容拆开,原本的内容用普通的主席树记,和BZOJ 2588一样,修改时候用树状数组,查的时候一起查,这样预处理的负责度就从Nlog^2(N)降到了NlogN。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 #define rint register int 6 7 int n, q, u, v, pow[100000]; 8 int k[100000], a[100000], b[100000]; 9 int begin[100000], end[100000]; 10 int fa[100000][22], depth[100000]; 11 12 char ch,B[1<<15],*S=B,*T=B,buf[1<<21],*O=buf,stk[40]; 13 #define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) 14 inline const int getint() //输入优化 15 { 16 char c = getc(); 17 rint k = 1, r = 0; 18 for(; c < '0' || c > '9'; c = getc()) 19 if(c == '-') k = -1; 20 for(; c >= '0' && c <= '9'; c = getc()) 21 r = r * 10 + c - '0'; 22 return k * r; 23 } 24 25 int lca(rint x, rint y) //找LCA 26 { 27 rint i; 28 if (depth[x] < depth[y]) 29 x ^= y, y ^= x, x ^= y; 30 for (i = fa[x][21]; depth[x] > depth[y]; i--) 31 if (depth[fa[x][i]] >= depth[y]) x = fa[x][i]; 32 if (x == y) return x; 33 for (i = fa[x][21]; i >= 0; i--) 34 if (fa[x][i] != fa[y][i]) 35 x = fa[x][i], y = fa[y][i]; 36 return fa[x][0]; 37 38 } 39 40 /*********edges**********/ 41 int ep[200000], et[200000]; 42 int last[100000], en; 43 44 inline void addedge(rint f, rint t) //邻接表 45 { 46 en++; 47 ep[en] = last[f]; 48 last[f] = en; 49 et[en] = t; 50 } 51 52 /*****Discretization*****/ 53 int num[200000], count; 54 55 int lb(rint key) // lower_bound 56 { 57 rint l = 1, r = count, mid; 58 while (l < r) 59 { 60 mid = l + r >> 1; 61 if (num[mid] == key) 62 return mid; 63 if (num[mid] < key) 64 l = mid + 1; 65 else r = mid - 1; 66 } 67 return l; 68 } 69 70 void discretizate() //离散化 71 { 72 std::sort(num + 1, num + count + 1); 73 rint tmp = 0, i; 74 num[++count] = 1000000013; 75 for (i = 1; i <= count; i++) 76 { 77 if (num[i] != num[i + 1]) 78 num[++tmp] = num[i]; 79 } 80 count = tmp; 81 for (i = 1; i <= n; i++) 82 { 83 pow[i] = lb(pow[i]); 84 } 85 } 86 87 /********ZX-Tree*********/ 88 int nls[10000000], nrs[10000000], nsum[10000000]; 89 90 int ns, tmpos, tmdata; 91 92 int tmodify(rint left, rint right, rint together) //主席树上修改 93 { 94 int i = ++ns; 95 if (left < right) 96 { 97 int mid = left + right >> 1; 98 if (tmpos <= mid) 99 { 100 nls[i] = tmodify(left, mid, nls[together]); 101 nrs[i] = nrs[together]; 102 } 103 else 104 { 105 nrs[i] = tmodify(mid + 1, right, nrs[together]); 106 nls[i] = nls[together]; 107 } 108 nsum[i] = nsum[nls[i]] + nsum[nrs[i]]; 109 } else nsum[i] = nsum[together] + tmdata; 110 return i; 111 } 112 113 /*******tree-array*******/ 114 int root[100000], rts, rootofN[100000]; 115 116 void modify(rint x, rint y, rint pos, rint data) //树状数组上修改一段区间 117 { 118 tmpos = pos, tmdata = data; 119 for (; x <= n; x += x & -x) 120 root[x] = tmodify(1, count, root[x]); 121 tmdata = -data; 122 for (y++; y <= n; y += y & -y) 123 root[y] = tmodify(1, count, root[y]); 124 } 125 126 int rs[4][30]; 127 128 int init_rs(rint i, rint x) //用rs数组记录查询时需要用到的点 129 { 130 memset(rs[i], 0, sizeof (rs[i])); 131 rs[i][++rs[i][0]] = rootofN[x]; 132 int ret = nsum[rootofN[x]]; 133 x = begin[x]; 134 for (; x > 0; x -= x & -x) 135 { 136 rs[i][++rs[i][0]] = root[x]; 137 ret += nsum[root[x]]; 138 } 139 return ret; 140 } 141 142 int query_rs() //查询每个点右儿子的大小 143 { 144 int ret = 0; rint i; 145 for (i = 1; i <= rs[0][0]; i++) 146 ret += nsum[nrs[rs[0][i]]]; 147 for (i = 1; i <= rs[1][0]; i++) 148 ret += nsum[nrs[rs[1][i]]]; 149 for (i = 1; i <= rs[2][0]; i++) 150 ret -= nsum[nrs[rs[2][i]]]; 151 for (i = 1; i <= rs[3][0]; i++) 152 ret -= nsum[nrs[rs[3][i]]]; 153 return ret; 154 } 155 156 void down(rint d) //将每个点变成它的左/右儿子 157 { 158 rint x, i; 159 for (x = 0; x < 4; x++) 160 for (i = 1; i <= rs[x][0]; i++) 161 rs[x][i] = d ? nrs[rs[x][i]] : nls[rs[x][i]]; 162 } 163 164 int query(rint x, rint y, rint data) //查询 165 { 166 int s = 0, f = lca(x, y), mid; 167 s += init_rs(0, x); 168 s += init_rs(1, y); 169 s -= init_rs(2, f); 170 s -= init_rs(3, fa[f][0]); 171 if (s < data) return -1; 172 int left = 1, right = count; 173 while (left < right) //迭代 174 { 175 mid = left + right >> 1; 176 s = query_rs(); 177 if (data > s) 178 { 179 down(0); 180 right = mid; 181 data -= s; 182 } 183 else 184 { 185 down(1); 186 left = mid + 1; 187 } 188 } 189 return num[left]; 190 } 191 192 /**********dfs***********/ 193 void DFS(rint x) //找出dfs序和求lca的rmq数组 194 { 195 rint i; 196 depth[x] = depth[fa[x][0]] + 1; 197 for (i = 0; fa[x][i]; fa[x][21] = ++i) 198 fa[x][i + 1] = fa[fa[x][i]][i]; 199 tmpos = pow[x], tmdata = 1; 200 rootofN[x] = tmodify(1, count, rootofN[fa[x][0]]); 201 begin[x] = ++rts; 202 for (i = last[x]; i; i = ep[i]) 203 if (begin[et[i]] == 0) 204 fa[et[i]][0] = x, DFS(et[i]); 205 end[x] = rts; 206 } 207 208 int main() 209 { 210 n = getint(); 211 q = getint(); 212 count = 0; 213 rint i, tmp; 214 for (i = 1; i <= n; i++) 215 num[++count] = pow[i] = getint(); 216 for (i = 1; i < n; i++) 217 { 218 u = getint(); 219 v = getint(); 220 addedge(u, v); 221 addedge(v, u); 222 } 223 for (i = 1; i <= q; i++) 224 { 225 k[i] = getint(); 226 a[i] = getint(); 227 b[i] = getint(); 228 if (k[i] == 0) num[++count] = b[i]; 229 } 230 discretizate(); 231 DFS(n / 2 + 1); 232 for (i = 1; i <= q; i++) 233 { 234 if (k[i]) 235 { 236 tmp = query(a[i], b[i], k[i]); 237 if (tmp >= 0) printf("%d\n", tmp); 238 else printf("invalid request!\n"); 239 } 240 else 241 { 242 modify(begin[a[i]], end[a[i]], pow[a[i]], -1); 243 pow[a[i]] = lb(b[i]); 244 modify(begin[a[i]], end[a[i]], pow[a[i]], 1); 245 } 246 } 247 }
这道题理论上主席树会有25000000+的节点,实际上不到10000000……