CF1059E Split the Tree(树形DP,倍增,树上DFS序二分)

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$ 结点的祖先

可以以更小的空间消耗和时间消耗完成这道题。

原文地址:https://www.cnblogs.com/PaulShi/p/10079008.html