1. 程式人生 > >Codeforces Round #514 (Div. 2) E. Split the Tree(貪心+倍增)

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;
}