UOJ462 新年的小黃鴨【線段樹,dp】
阿新 • • 發佈:2021-06-25
給定 \(n\) 個點的樹,要求一種樹剖方案使得"代價"儘可能小。
\(n\le 10^5\)。
設 \(f_u\) 表示只考慮 \(u\) 的子樹時的代價,列舉重鏈 \((u,v)\),其中 \(v\) 是葉子。
代價有兩部分,一部分是重鏈連出的子樹的 \(f+siz\) 之和,還有一部分是重鏈的代價:設重鏈是 \(u=a_0,\cdots,a_d=v\),則為 \(siz_{a_1}+\sum_{i\ge 0}siz_{a_{2^i+1}}\)。預處理出每個點的 \(1\) 和 \(2^i+1\) 級兒子,就可以那貢獻在自底而上的 dp 過程中處理成 dfs 序區間加。
按葉子 dfs 序建線段樹,維護區間加、區間求 \(\min\)
#include<bits/stdc++.h> #define PB emplace_back using namespace std; const int N = 100003, M = 1<<18; template<typename T> void rd(T &x){ int ch = getchar(); x = 0; for(;ch < '0' || ch > '9';ch = getchar()); for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0'; } int n, dfn[N], out[N], siz[N], tim, fa[17][N], seg[M], tag[M], dp[N]; vector<int> son[N], G[N]; void ptag(int x, int v){seg[x] += v; tag[x] += v;} void pdown(int x){ if(tag[x]){ ptag(x<<1, tag[x]); ptag(x<<1|1, tag[x]); tag[x] = 0; return; } } void upd(int l, int r, int v, int x = 1, int L = 1, int R = tim){ if(l <= L && R <= r){ptag(x, v); return;} int md = L+R>>1; pdown(x); if(l <= md) upd(l, r, v, x<<1, L, md); if(md < r) upd(l, r, v, x<<1|1, md+1, R); seg[x] = min(seg[x<<1], seg[x<<1|1]); } int qry(int l, int r, int x = 1, int L = 1, int R = tim){ if(l <= L && R <= r) return seg[x]; int md = L+R>>1; pdown(x); if(r <= md) return qry(l, r, x<<1, L, md); if(md < l) return qry(l, r, x<<1|1, md+1, R); return min(qry(l, r, x<<1, L, md), qry(l, r, x<<1|1, md+1, R)); } void dfs1(int x){ siz[x] = 1; for(int i = 0;i < 16;++ i) fa[i+1][x] = fa[i][fa[i][x]]; if(x > 1) son[fa[0][x]].PB(x); for(int i = 0, v;i < 17;++ i) if(v = fa[0][fa[i][x]]) son[v].PB(x); dfn[x] = tim+1; if(x > 1 && G[x].size() == 1) ++tim; for(int v : G[x]) if(v != fa[0][x]){ fa[0][v] = x; dfs1(v); siz[x] += siz[v]; } out[x] = tim; } void dfs2(int x){ int sum = 0; for(int v : G[x]) if(v != fa[0][x]){ dfs2(v); sum += siz[v] + dp[v]; } for(int v : G[x]) if(v != fa[0][x]) upd(dfn[v], out[v], sum-siz[v]-dp[v]); for(int v : son[x]) upd(dfn[v], out[v], siz[v]); dp[x] = qry(dfn[x], out[x]); for(int v : son[x]) upd(dfn[v], out[v], -siz[v]); } int main(){ rd(n); for(int i = 1, u, v;i < n;++ i){ rd(u); rd(v); G[u].PB(v); G[v].PB(u); } dfs1(1); dfs2(1); printf("%d\n", dp[1]); }