题意:给定两棵树T1,T2,求d1[x] + d1[y] - d1[lca1(x, y)] - d2[lca2(x, y)]的最大值。
解:考虑把上面这个毒瘤东西化一下。发现它就是T1中x,y到根的路径并 - T2中x,y到根的路径交。
也就是T1中的一个三叉状路径和T2中lca到根的路径。
根据情报中心的套路,把这个东西 * 2,发现就是d1[x] + d1[y] + dis1(x, y) - d2[x] - d2[y] + dis2(x, y)
令val[i] = d1[i] - d2[i],我们就要令val[x] + val[y] + dis1(x, y) + dis2(x, y)最大。
考虑在T1上边分治,这样的话dis1(x, y) = d[x] + d[y] + e。e是分治边的长度。
在T2上把T1这次分治的点全部提出来建虚树。我们不妨把每个T1的节点挂到对应的虚树节点上,边权为val[x] + d[x],那么我们就是要在虚树上挂的点中找到两个异色节点,使得它们距离最远。
此时我们有一个非常优秀的O(n)DFS算法......但是我就是SB中的SB中的SB,把这个问题搞成了nlogn。这个解决之后本题就做完了。为了卡常用了优秀的zkw线段树。
1 #include <bits/stdc++.h> 2 3 #define forson(x, i) for(register int i = e[x]; i; i = edge[i].nex) 4 5 #define add(x, y, z) edge[++tp].v = y; edge[tp].len = z; edge[tp].nex = e[x]; e[x] = tp 6 #define ADD(x, y) EDGE[++TP].v = y; EDGE[TP].nex = E[x]; E[x] = TP 7 8 typedef long long LL; 9 const int N = 800010; 10 const LL INF = 4e18; 11 12 inline char gc() { 13 static char buf[N], *p1(buf), *p2(buf); 14 if(p1 == p2) p2 = (p1 = buf) + fread(buf, 1, N, stdin); 15 return p1 == p2 ? EOF : *p1++; 16 } 17 18 template <class T> inline void read(T &x) { 19 x = 0; 20 register char c(gc()); 21 bool f(false); 22 while(c < '0' || c > '9') { 23 if(c == '-') f = true; 24 c = gc(); 25 } 26 while(c >= '0' && c <= '9') { 27 x = x * 10 + c - 48; 28 c = gc(); 29 } 30 if(f) x = (~x) + 1; 31 return; 32 } 33 34 struct Edge { 35 int nex, v; 36 bool vis; 37 LL len; 38 }edge[N << 1], EDGE[N << 1]; int tp = 1, TP; 39 40 int pw[N << 1], n, e[N], Cnt, _n, small, root, imp[N], K, use[N], Time, stk[N], top, E[N], RT, POS[N], NUM, SIZ[N], ID[N], vis[N], siz[N]; 41 LL val[N], ans = -INF, d[N], VAL[N], H[N], C[N], W[N]; 42 std::vector<int> G[N]; 43 std::vector<LL> Len[N]; 44 45 namespace seg2 { 46 LL large[N << 2]; 47 void build(int l, int r, int o) { 48 if(l == r) { 49 large[o] = VAL[ID[r]]; 50 //if(F) printf("p = %d x = %d val = %lld ", r, ID[r], VAL[ID[r]]); 51 return; 52 } 53 int mid((l + r) >> 1); 54 build(l, mid, o << 1); 55 build(mid + 1, r, o << 1 | 1); 56 large[o] = std::max(large[o << 1], large[o << 1 | 1]); 57 return; 58 } 59 LL getMax(int L, int R, int l, int r, int o) { 60 if(L <= l && r <= R) { 61 return large[o]; 62 } 63 LL ans = -INF; 64 int mid((l + r) >> 1); 65 if(L <= mid) ans = getMax(L, R, l, mid, o << 1); 66 if(mid < R) ans = std::max(ans, getMax(L, R, mid + 1, r, o << 1 | 1)); 67 return ans; 68 } 69 } 70 71 namespace seg { 72 LL large[N << 2]; 73 int lm; 74 inline void build(int n) { 75 lm = 1; n += 2; 76 while(lm < n) lm <<= 1; 77 large[lm] = -INF; 78 for(register int i = 1; i <= n; i++) { 79 large[i + lm] = VAL[ID[i]]; 80 } 81 for(register int i = n + 1; i < lm; i++) { 82 large[i + lm] = -INF; 83 } 84 for(int i = lm - 1; i >= 1; i--) { 85 large[i] = std::max(large[i << 1], large[i << 1 | 1]); 86 } 87 return; 88 } 89 inline LL getMax(int L, int R) { 90 LL ans = -INF; 91 for(int s = lm + L - 1, t = lm + R + 1; s ^ t ^ 1; s >>= 1, t >>= 1) { 92 if(t & 1) { 93 ans = std::max(ans, large[t ^ 1]); 94 } 95 if((s & 1) == 0) { 96 ans = std::max(ans, large[s ^ 1]); 97 } 98 } 99 return ans; 100 } 101 } 102 103 namespace t1 { 104 int e[N], pos2[N], ST[N << 1][20], num2, deep[N]; 105 LL d[N]; 106 Edge edge[N << 1]; int tp; 107 void DFS_1(int x, int f) { 108 deep[x] = deep[f] + 1; 109 pos2[x] = ++num2; 110 ST[num2][0] = x; 111 forson(x, i) { 112 int y(edge[i].v); 113 if(y == f) continue; 114 d[y] = d[x] + edge[i].len; 115 DFS_1(y, x); 116 ST[++num2][0] = x; 117 } 118 return; 119 } 120 inline void prework() { 121 for(register int j(1); j <= pw[num2]; j++) { 122 for(register int i(1); i + (1 << j) - 1 <= num2; i++) { 123 if(deep[ST[i][j - 1]] < deep[ST[i + (1 << (j - 1))][j - 1]]) { 124 ST[i][j] = ST[i][j - 1]; 125 } 126 else { 127 ST[i][j] = ST[i + (1 << (j - 1))][j - 1]; 128 } 129 } 130 } 131 return; 132 } 133 inline int lca(int x, int y) { 134 x = pos2[x], y = pos2[y]; 135 if(x > y) std::swap(x, y); 136 int t(pw[y - x + 1]); 137 if(deep[ST[x][t]] < deep[ST[y - (1 << t) + 1][t]]) { 138 return ST[x][t]; 139 } 140 else { 141 return ST[y - (1 << t) + 1][t]; 142 } 143 } 144 inline LL dis(int x, int y) { 145 return d[x] + d[y] - 2 * d[lca(x, y)]; 146 } 147 } 148 149 void rebuild(int x, int f) { 150 int temp(0); 151 for(register int i(0); i < G[x].size(); i++) { 152 int y = G[x][i]; 153 LL len = Len[x][i]; 154 if(y == f) continue; 155 if(!temp) { 156 add(x, y, len); 157 add(y, x, len); 158 temp = x; 159 } 160 else if(i == G[x].size() - 1) { 161 add(temp, y, len); 162 add(y, temp, len); 163 } 164 else { 165 add(temp, ++Cnt, 0); 166 add(Cnt, temp, 0); 167 temp = Cnt; 168 add(temp, y, len); 169 add(y, temp, len); 170 } 171 d[y] = d[x] + len; 172 rebuild(y, x); 173 } 174 return; 175 } 176 177 void getroot(int x, int f) { 178 siz[x] = 1; 179 //printf("getroot : x = %d ", x); 180 forson(x, i) { 181 int y(edge[i].v); 182 if(y == f || edge[i].vis) continue; 183 getroot(y, x); 184 if(small > std::max(siz[y], _n - siz[y])) { 185 small = std::max(siz[y], _n - siz[y]); 186 root = i; 187 } 188 siz[x] += siz[y]; 189 } 190 //printf("siz %d = %d ", x, siz[x]); 191 return; 192 } 193 194 void DFS_1(int x, int f, int flag) { 195 siz[x] = 1; 196 //printf("x = %d d = %lld ", x, d[x]); 197 if(!flag && x <= n) { 198 imp[++K] = x; 199 vis[x] = Time; 200 //if(F) printf("vis %d = Time ", x); 201 } 202 else if(flag && x <= n) { 203 imp[++K] = x; 204 } 205 forson(x, i) { 206 int y(edge[i].v); 207 if(y == f || edge[i].vis) continue; 208 d[y] = d[x] + edge[i].len; 209 DFS_1(y, x, flag); 210 siz[x] += siz[y]; 211 } 212 return; 213 } 214 215 inline void work(int x) { 216 if(use[x] == Time) return; 217 use[x] = Time; 218 E[x] = 0; 219 return; 220 } 221 222 inline bool cmp(const int &a, const int &b) { 223 return t1::pos2[a] < t1::pos2[b]; 224 } 225 226 inline void build_t() { 227 std::sort(imp + 1, imp + K + 1, cmp); 228 K = std::unique(imp + 1, imp + K + 1) - imp - 1; 229 work(imp[1]); 230 stk[top = 1] = imp[1]; 231 for(register int i(2); i <= K; i++) { 232 int x = imp[i], y = t1::lca(x, stk[top]); 233 work(x); work(y); 234 while(top > 1 && t1::pos2[y] <= t1::pos2[stk[top - 1]]) { 235 ADD(stk[top - 1], stk[top]); 236 top--; 237 } 238 if(y != stk[top]) { 239 ADD(y, stk[top]); 240 stk[top] = y; 241 } 242 stk[++top] = x; 243 } 244 while(top > 1) { 245 ADD(stk[top - 1], stk[top]); 246 top--; 247 } 248 RT = stk[top]; 249 return; 250 } 251 252 void dfs_2(int x) { 253 //printf("dfs_2 x = %d ", x); 254 SIZ[x] = 1; 255 POS[x] = ++NUM; 256 ID[NUM] = x; 257 if(vis[x] == Time) VAL[x] = t1::d[x] + val[x] + d[x]; 258 else VAL[x] = -INF; 259 /*if(F && x == 3) { 260 printf("VAL 3 = %lld + %lld + %lld ", t1.d[x], val[x], d[x]); 261 }*/ 262 C[x] = VAL[x]; 263 for(register int i(E[x]); i; i = EDGE[i].nex) { 264 int y = EDGE[i].v; 265 dfs_2(y); 266 SIZ[x] += SIZ[y]; 267 C[x] = std::max(C[x], C[y]); 268 } 269 //if(F) printf("x = %d VAL = %lld C = %lld ", x, VAL[x], C[x]); 270 return; 271 } 272 273 void dfs_3(int x, int f) { 274 if(f) { 275 /// cal H[x] 276 H[x] = seg::getMax(POS[f], POS[x] - 1); 277 if(POS[x] + SIZ[x] < POS[f] + SIZ[f]) { 278 H[x] = std::max(H[x], seg::getMax(POS[x] + SIZ[x], POS[f] + SIZ[f] - 1)); 279 } 280 W[x] = std::max(W[f], H[x] - 2 * t1::d[f]); 281 } 282 else { 283 H[x] = W[x] = -INF; 284 } 285 //if(F) printf("x = %d H = %lld W = %lld ", x, H[x], W[x]); 286 for(register int i(E[x]); i; i = EDGE[i].nex) { 287 int y(EDGE[i].v); 288 dfs_3(y, x); 289 } 290 return; 291 } 292 293 void DFS_4(int x, int f, LL v) { 294 /// ask 295 if(x <= n) { 296 VAL[x] = t1::d[x] + val[x] + d[x] + v; 297 ans = std::max(ans, VAL[x] + W[x]); 298 ans = std::max(ans, VAL[x] + C[x] - 2 * t1::d[x]); 299 } 300 forson(x, i) { 301 int y(edge[i].v); 302 if(y == f || edge[i].vis) continue; 303 DFS_4(y, x, v); 304 } 305 return; 306 } 307 308 void e_div(int x) { 309 if(_n == 1) { 310 ans = std::max(ans, val[x] << 1); 311 return; 312 } 313 small = N; 314 getroot(x, 0); 315 edge[root].vis = edge[root ^ 1].vis = 1; 316 317 x = edge[root].v; 318 int y = edge[root ^ 1].v; 319 if(siz[x] < _n - siz[x]) std::swap(x, y); 320 321 //if(F) printf("div %d %d _n = %d ", x, y, _n); 322 323 /// 324 d[x] = d[y] = 0; 325 TP = K = 0; 326 Time++; 327 DFS_1(x, 0, 0); 328 DFS_1(y, 0, 1); 329 /// build virtue trEE 330 build_t(); 331 //printf("v_t build over "); 332 /// prework 333 NUM = 0; 334 dfs_2(RT); 335 //printf("dfs_2 over "); 336 seg::build(NUM); 337 //printf("seg build over "); 338 dfs_3(RT, 0); 339 DFS_4(y, 0, edge[root].len); 340 341 //if(F) printf("ans = %d ", ans); 342 343 _n = siz[x]; 344 e_div(x); 345 _n = siz[y]; 346 e_div(y); 347 return; 348 } 349 350 int main() { 351 352 W[0] = -INF; 353 read(n); 354 LL z; 355 for(register int i(1), x, y; i < n; i++) { 356 read(x); read(y); read(z); 357 G[x].push_back(y); 358 G[y].push_back(x); 359 Len[x].push_back(z); 360 Len[y].push_back(z); 361 } 362 for(register int i(1), x, y; i < n; i++) { 363 read(x); read(y); read(z); 364 t1::edge[++t1::tp].v = y; 365 t1::edge[t1::tp].len = z; 366 t1::edge[t1::tp].nex = t1::e[x]; 367 t1::e[x] = t1::tp; 368 t1::edge[++t1::tp].v = x; 369 t1::edge[t1::tp].len = z; 370 t1::edge[t1::tp].nex = t1::e[y]; 371 t1::e[y] = t1::tp; 372 } 373 for(register int i = 2; i <= n * 2; i++) { 374 pw[i] = pw[i >> 1] + 1; 375 } 376 t1::DFS_1(1, 0); 377 t1::prework(); 378 379 Cnt = n; 380 rebuild(1, 0); 381 for(register int i(1); i <= n; i++) { 382 val[i] = d[i] - t1::d[i]; 383 } 384 /// ans = val[i] + val[j] + t0.dis(i, j) + t1.dis(i, j); 385 _n = Cnt; 386 e_div(1); 387 388 printf("%lld ", ans >> 1); 389 return 0; 390 }
讲一下我的做法。考虑到两个点的距离是d + d - 2 * lca,我们预处理某一颜色的节点,然后枚举另一个颜色的所有点。
由于开店的启发,我们可以把每个点往上更新链,然后每个点往上查询链。
进而发现,查询全是静态的,所以可以预处理出一个点到根路径上的max,变成单点查询。
设VAl[x]为挂上的节点x的深度。C[x]为x的子树中的最大VAL值。
设H[x]为(subtree(fa[x]) subtree(x)的最大VAL值) - 2 * VAL[x],W[x]为x到根路径上的最大H值。
那么对于一个点x,它只要和W[x],C[x] - 2 * VAL[x]拼起来就能得到跟它相关的最远点对距离了。
复杂度在于求H[x]的时候使用DFS序 + RMQ,写了一个log。所以总复杂度是nlog2n。
有个T1是链且v > 0的部分分。这里我们发现T1的路径并变成了max(d1[x], d1[y]),于是考虑枚举T2中每个点作为lca,要求两个点在其子树中,且max(d1[x], d1[y])最大。显然维护一个子树最大值就好了。实测有80分呢......
还有个迭代乱搞是随机选一个起点,每次迭代找到一个点与之贡献最大并更新那个点为新的起点。多来几次。这样也有80分呢......
比写正解不知道高到哪里去了...