Codeforces Round #514 (Div. 2) E. Split the Tree(貪心+倍增)
阿新 • • 發佈:2018-11-08
題意:給你一棵樹,問你最多能把這棵樹分成多少條鏈,使得每條鏈的長度不超過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; }