[BalticOI 2017] Cat in a tree
阿新 • • 發佈:2022-03-26
[BalticOI 2017] Cat in a tree
神仙美少女 Tweetuzki 學姐用了長剖+線段樹,私以為長剖可以做到線性。
簡要題意
-
給定 \(n\) 個點的樹,點集 \(S\) 合法當且僅當 \(\forall u,v\in S, dis(u,v)\ge k\)。求 \(\max |S|\)。
-
\(n \le 2\times 10^5\)。
題解
記 \(f_{u,i}\) 表示以 \(u\) 為根的子樹中,選擇的深度最淺的點深度大於等於 \(i\) 的最大點集大小。此處深度針對這一子樹而言。轉移考慮加入一棵子樹,方程比較簡單:更新 \(f_{u,j}\) 時,記 \(t = \max(j,k-j)\)
注意到當 \(j \gt dep_u\) 時,\(f_{u,j}=0\)。所以轉移時只需要列舉到 \(dep_v\) 即可。這啟發我們使用長剖優化。
程式碼
注意邊界細節。
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 200010; int n, K; struct edge{ int ne, to; edge(int N=0,int T=0):ne(N),to(T){} }e[MAXN<<1]; int fir[MAXN], num = 0; inline void join(int a, int b) { e[++num] = edge(fir[a], b); fir[a] = num; } int buf[MAXN], *f[MAXN], *now = buf, dep[MAXN], son[MAXN], fa[MAXN]; void predfs(int u, int pa) { fa[u] = pa; for(int i=fir[u],v;i;i=e[i].ne) if((v = e[i].to) != pa) { predfs(v, u); dep[u] = max(dep[u], dep[v]+1); if(dep[son[u]] < dep[v]) son[u] = v; } dep[u] = dep[son[u]] + 1; } void dfs(int u) { f[u][0] = 1; if(son[u]) { f[son[u]] = f[u] + 1; dfs(son[u]); if(K-1 < dep[son[u]]) f[u][0] += f[son[u]][K-1]; f[u][0] = max(f[u][1], f[u][0]); } for(int i=fir[u],v;i;i=e[i].ne) if((v=e[i].to) != fa[u] && v != son[u]) { f[v] = now; now += dep[v]; dfs(v); for(int j=0;j<=dep[v];j++) { int k = max(j, K-j); f[u][j] = max(((k) ? (j<dep[u]?f[u][j]:0) + (k<=dep[v]?f[v][k-1]:0) : 0), (j ? (k<dep[u]?f[u][k]:0) + f[v][j-1] : 0)); } for(int j=dep[v];~j;j--) f[u][j] = max(f[u][j], j==dep[u]-1?0:f[u][j+1]); } } int main() { scanf("%d%d",&n,&K); for(int i=2,x;i<=n;i++) { scanf("%d",&x); join(i, ++x); join(x, i); } predfs(1, 0); f[1] = now; now += dep[1]; dfs(1); printf("%d\n",f[1][0]); return 0; }