CF1059E Split the Tree(树形DP,倍增,树上DFS序二分)
题目链接:CF1059E
我们可以先倍增预处理出从每个结点向上最多能延伸多长,用 $len[u]$ 表示
我们再观察一下DP方程 $f[u]=max (f[v])$ , $num[u]=sum num[v]$ $(fa[v]=u)$
如果当前点 $f[u]=-1$,则 $f[u]=len[u]-1,num[u]++$
#include<bits/stdc++.h> #define MAXN 100010 using namespace std; typedef long long ll; inline ll read () { ll s=0,w=1; char ch=getchar (); while (ch<'0'||ch>'9'){if (ch=='-') w=-1;ch=getchar ();} while ('0'<=ch&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar (); return s*w; } struct edge{ int v,nxt; }e[MAXN<<1]; int n,l,cnt; ll s,w[MAXN],dis[MAXN]; int head[MAXN],mi[21]; int F[MAXN][21],dep[MAXN],fa[MAXN],len[MAXN]; int f[MAXN],num[MAXN]; void add (int u,int v) { e[++cnt].v=v; e[cnt].nxt=head[u]; head[u]=cnt; } void dfs (int u,int ff) { dis[u]=dis[ff]+w[u],dep[u]=dep[ff]+1; F[u][0]=ff,fa[u]=ff; for (int i=1;mi[i]<=dep[u];i++) F[u][i]=F[F[u][i-1]][i-1]; for (int i=head[u];i!=0;i=e[i].nxt) dfs (e[i].v,u); } void DFS (int u) { f[u]=-1; for (int i=head[u];i!=0;i=e[i].nxt) { DFS (e[i].v); num[u]+=num[e[i].v]; f[u]=max (f[u],f[e[i].v]-1); } if (f[u]==-1) f[u]=len[u]-1,num[u]++; } int main() { mi[0]=1;for (int i=1;i<=20;i++) mi[i]=mi[i-1]<<1; n=read (),l=read (),s=read (); for (int i=1;i<=n;i++) { w[i]=read (); if (w[i]>s) { printf ("-1"); return 0; } } for (int i=2;i<=n;i++) add (read (),i); dfs (1,0); for (int i=1;i<=n;i++) { int u=i; for (int j=20;j>=0;j--) if (F[u][j]!=0&&dis[i]-dis[fa[F[u][j]]]<=s&&dep[i]-dep[fa[F[u][j]]]<=l) u=F[u][j]; len[i]=dep[i]-dep[u]+1; } DFS (1); printf ("%d",num[1]); return 0; }
在这里我们可以发现一个优美的性质
我们可以按照dfs栈的顺序,在栈里二分,因为在 $u$ 时,栈中存的是有序的 $u$ 结点的祖先
可以以更小的空间消耗和时间消耗完成这道题。