题意:给你一棵树,问你最多能把这棵树分成多少条链,使得每条链的长度不超过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;
}