CF1059E Split the Tree(倍增)

题意翻译

现有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\n",ans);
48     return 0;
49 }

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9761742.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值