Codeforces Round #514 (Div. 2) E. Split the Tree(贪心+倍增)

题意:给你一棵树,问你最多能把这棵树分成多少条链,使得每条链的长度不超过L,每条链上的点的权值和不超过S。

思路:这题是参考别人的思路,等有实力了自己再试试。。。从叶子往上贪心,每一次取能达到的最长链,也就是尽可能走到最远的父亲那里,这里采用树上倍增处理,用top记录每个节点最远能去哪,然后从下到上遍历,贪心的取最远的节点。

具体看代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN=100005;
typedef long long ll;
 
vector<int> G[MAXN];
int fa[25][MAXN];
int dep[MAXN];
int top[MAXN];//每个节点最远能去到的那个点
int W[MAXN];
ll sum[MAXN];//从上往下走的节点值的和
int son[MAXN];//每个节点取的那个最远能走到的那个点
ll N,L,S;
 
//倍增处理节点的第2^k个祖先是哪个
void initST(){
    fa[0][1]=-1;
    for(int k=0;k<20;k++)
        for(int i=1;i<=N;i++)
            if(fa[k][i]<0)
                fa[k+1][i]=-1;
            else
                fa[k+1][i]=fa[k][fa[k][i]];
}
 
//求出top,sum数组
void dfs1(int u,int pre){
    sum[u]=sum[pre]+W[u];
    dep[u]=dep[pre]+1;
    top[u]=u;
    //倍增找能去到的最远的点
    int dis=L;
    for(int k=20;k>=0;k--){
        int f=fa[k][top[u]];
        if((1<<k)>=dis||f==-1)//判断L是否满足
            continue;
        if(sum[u]-sum[f]+W[f]>S)//判断S是否满足
            continue;
        dis-=(1<<k);
        top[u]=f;//更新
    }
    for(int i=0;i<G[u].size();i++)
        dfs1(G[u][i],u);
}
 
//求出son数组并记录答案
int ans=0;
void dfs2(int u){
    int best=-1;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        dfs2(v);
        if(son[v]==v)//这个很重要,相当于把已经确定的链都删掉
            continue;
        if(best==-1||dep[best]>dep[son[v]])//保存最远的那个
            best=son[v];
    }
    if(best==-1){
        best=top[u];
        ans++;
    }
    son[u]=best;
}
 
int main(){
    
    scanf("%lld%lld%lld",&N,&L,&S);
    for(int i=1;i<=N;i++){
        scanf("%lld",&W[i]);
        if(W[i]>S)
        {
            cout<<-1<<endl;
            return 0;
        }
    }
    int tmp;
    for(int i=2;i<=N;i++)
    {
        scanf("%d",&tmp);
        fa[0][i]=tmp;
        G[tmp].push_back(i);
    }
    
    initST();
    dfs1(1,0);
    dfs2(1);
    cout<<ans<<endl;
    return 0;
}
 

猜你喜欢

转载自blog.csdn.net/Dilly__dally/article/details/83038839