[CodeForces1059E] Split the Tree

树形DP.

用倍增处理出来每个点往上能延伸出去的最远路径,nlogn

对于每个节点,如果它能被后代使用过的点覆盖,就直接覆盖,这个点就不使用,否则就ans++,让传的Max改成dp[x]

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=100005;
int n,a[N],fa[N][20],f[N],head[N],to[N<<1],nxt[N<<1],ecnt,ans,L;
inline void add(int bg,int ed) {
    nxt[++ecnt]=head[bg];to[ecnt]=ed;head[bg]=ecnt;
}
ll S,v[N][20];
void dfs(int x) {
    for(int i=head[x];i;i=nxt[i]) 
        if(to[i]!=fa[x][0]) fa[to[i]][0]=x,v[to[i]][0]=a[x],dfs(to[i]);
}
int Dfs(int x) {
    int Max=0;
    for(int i=head[x];i;i=nxt[i]) if(to[i]!=fa[x][0]) Max=max(Max,Dfs(to[i]));
    if(!Max) {
        ans++;
        return f[x]-1;
    }
    return Max-1;
}
int main() {
    cin>>n>>L>>S;
    for(int i=1;i<=n;i++) {scanf("%d",&a[i]);if(a[i]>S) return puts("-1"),0;}
    for(int i=1,x;i<n;i++)     scanf("%d",&x),add(x,i+1),add(i+1,x);
    dfs(1);

    for(int i=1;(1<<i)<=L;i++) {
        for(int j=1;j<=n;j++) {
            fa[j][i]=fa[fa[j][i-1]][i-1];
            v[j][i]=v[fa[j][i-1]][i-1]+v[j][i-1];
        }
    }
    for(int i=1;i<=n;i++) {
        ll sum=a[i];int now=i;f[i]=1;
        for(int j=19;~j&&sum<=S;j--) {
            if(fa[now][j]&&sum+v[now][j]<=S&&f[i]+(1<<j)<=L) 
                            sum+=v[now][j],f[i]+=1<<j,now=fa[now][j];
        }
    }
    Dfs(1);
    cout<<ans;
    return 0;
}    
Split the Tree
我是咸鱼。转载博客请征得博主同意Orz
原文地址:https://www.cnblogs.com/sdfzhsz/p/9770453.html