【題解】[CCO 2019] Winter Driving
阿新 • • 發佈:2022-04-10
給定一顆樹,你需要給每條邊固定一個方向,使二元組 \((x,y)\) 總數最大,\(x,y\) 滿足存在一條從 \(x\) 出發到 \(y\) 的路徑。
很厲害的結論題。
根據樣例,我們猜測答案是以一個點為根的內向樹或外向樹,但是這非常的假。因為除了樹退化成鏈的情況,其它都不是最優。
我們手玩一下菊花圖,發現肯定是選擇一些邊從中心出發,其它邊則出發到中心。
結論一:對於菊花圖,最優解一定是將所有非中心點分為兩部分,使得兩部分 \(\sum A_i\) 的差最小。
顯然,我們要在和不變的情況下使乘積最大,就是均值不等式。兩邊儘量均分。
怎麼均分,直接揹包即可。
由此我們猜測,對於任意一顆樹,是否也像菊花圖一樣存在一箇中心點呢?
結論二:對於樹,最優解存在一箇中心點,將該點刪去後,剩下的子樹都是內向樹或者外向樹。
不大會證明,感覺挺對的。對於一個內向樹/外向樹,答案可以很方便一邊 DFS 求出。
那麼我們怎麼計算跨中心點的答案。根據結論一,我們是將這些子樹分成兩部分,使得 \(\sum A_i\) 的差最小即可。
這樣已經能拿到 \(40\) 分了。我們在觀察一些性質。
結論三:中心點一定是樹的重心。注意:是原樹的重心而不是對於 \(A\) 的加權重心。
這不難理解,本質還是均值不等式。但是我不大會嚴謹證明。
一棵樹最多隻有兩個重心,所以等價於我們只用求兩次菊花圖的答案然後取最大值。
由於值域比較大,不太能揹包。但是題目有性質 \(D\le 36\)
#define N 270005 int n, sz[N], a[N], X, Y; LL sum, ans, mx; vector<int>e[N]; void get(int x,int fa){ sz[x] = 1; int cur = 0; go(y, e[x])if(y != fa)get(y, x), sz[x] += sz[y], cmx(cur, sz[y]); cmx(cur, n - sz[x]); if(cur * 2 <= n)Y = x, swap(X, Y); } void dfs(int x,int fa){ sz[x] = 0; go(y, e[x])if(y != fa)dfs(y, x), sz[x] += sz[y]; sum += a[x] * 1LL * (sz[x] + a[x] - 1), sz[x] += a[x]; } int u[40], p[N], q[N], l, r; void ins1(int x,int s){ if(x == 19)p[++l] = s; else{ if(u[x])ins1(x + 1, s + u[x]); ins1(x + 1, s); } } void ins2(int x,int s){ if(x == 37)q[++r] = s; else{ if(u[x])ins2(x + 1, s + u[x]); ins2(x + 1, s); } } LL calc(int x){ int t = 0; mx = 0, l = r = 0; memset(u, 0, sizeof(u)); go(y, e[x])u[++t] = sz[y]; ins1(1, 0), ins2(19, 0); sort(p + 1, p + l + 1), sort(q + 1, q + r + 1); l = unique(p + 1, p + l + 1) - p - 1, r = unique(q + 1, q + r + 1) - q - 1; int lim = (sz[x] - a[x]) / 2; rp(i, l){ int j = lower_bound(q + 1, q + r + 1, lim - p[i]) - q; if(j <= r)cmx(mx, (p[i] + q[j]) * 1LL * (sz[x] - a[x] - p[i] - q[j])); }return mx; } void check(int x){sum = 0, dfs(x, 0), cmx(ans, sum + calc(x));} int main() { read(n); rp(i, n)read(a[i]); rp(i, n - 1){ int x; read(x); e[x].pb(i + 1), e[i + 1].pb(x); } get(1, 0); check(X); if(Y)check(Y); printf("%lld\n", ans); return 0; }