题意翻译
现有n个点组成一棵以1为根的有根树,第i个点的点权为wi,需将其分成若干条垂直路径使得每一个点当且仅当被一条垂直路径覆盖,同时,每条垂直路径长度不能超过L,点权和不能超过S,求最少需要几条垂直路径才能满足要求。特别地,无解输出-1。
一条垂直路径是一条包含v1, v2...vk的路径,使得vi(i>=2)是vi-1的父亲。
题解
话说莫不是树上的题目全都可以用倍增艹过去……
首先,这个东西是可以贪心的,我们每一次一定是选取子树里能向上拓展最多的点与自己连成一条链
然后每一个节点最多能向上拓展多少可以用倍增预处理出来
然后直接在dfs的时候判断即可
一开始也想到要贪心了……然而最后倍增那一步没想到233
1 //minamoto 2 #include<bits/stdc++.h> 3 #define ll long long 4 using namespace std; 5 const int N=100005; 6 int head[N],Next[N],ver[N],tot; 7 inline void add(int u,int v){ 8 ver[++tot]=v,Next[tot]=head[u],head[u]=tot; 9 } 10 int pa[N][25],top[N],son[N],dep[N],ans,n,L;ll sum[N],w[N],S; 11 inline void init(int u){ 12 for(int i=1;i<=20;++i) 13 pa[u][i]=pa[pa[u][i-1]][i-1]; 14 int dis=L,tmp=u; 15 for(int i=20;i>=0;--i){ 16 int fa=pa[tmp][i]; 17 if(!fa||(1<<i)>=dis) continue; 18 if(sum[u]-sum[fa]+w[fa]>S) continue; 19 dis-=(1<<i),top[u]=fa,tmp=fa; 20 } 21 } 22 void dfs1(int u,int fa){ 23 sum[u]=sum[fa]+w[u],top[u]=u,dep[u]=dep[fa]+1,init(u); 24 for(int i=head[u];i;i=Next[i]) 25 dfs1(ver[i],u); 26 } 27 void dfs2(int u){ 28 int mx=-1; 29 for(int i=head[u];i;i=Next[i]){ 30 int v=ver[i];dfs2(v); 31 if(son[v]==v) continue; 32 if(mx==-1||dep[mx]>dep[son[v]]) mx=son[v]; 33 } 34 if(mx==-1) ++ans,mx=top[u]; 35 son[u]=mx; 36 } 37 int main(){ 38 // freopen("testdata.in","r",stdin); 39 scanf("%d%d%lld",&n,&L,&S); 40 for(int i=1;i<=n;++i){ 41 scanf("%lld",&w[i]); 42 if(w[i]>S) return puts("-1"),0; 43 } 44 for(int i=2,f;i<=n;++i) 45 scanf("%d",&f),add(f,i),pa[i][0]=f; 46 dfs1(1,0),dfs2(1); 47 printf("%d ",ans); 48 return 0; 49 }