有N条绳子编号 0 至 N - 1,每条绳子后面栓了一个重物重量为Wi,绳子的最大负重为Ci。每条绳子或挂在别的绳子下或直接挂在钩子上(编号-1)。如果绳子下所有重物的重量大于绳子的最大负重就会断掉(等于不会断)。依次给出每条绳子的负重Ci、重物的重量Wi以及绳子会挂在之前的哪条绳子的下面,问最多挂多少个绳子而不会出现绳子断掉的情况。
例如下图:
5, 2, -1
3, 3, 0
6, 1, -1
3, 1, 0
3, 2, 3

挂到第4个时会有绳子断掉,所以输出3。

Input
第1行:1个数N,表示绳子的数量(1 <= N <= 50000)。 第2 - N + 1行:每行3个数,Ci, Wi, Pi,Ci表示最大负重,Wi表示重物的重量,Pi表示挂在哪个绳子上,如果直接挂在钩子上则Pi = -1(1 <= Ci <= 10^9,1 <= Wi <= 10^9,-1 <= Pi <= N - 2)。
Output
输出1个数,最多挂到第几个绳子,不会出现绳子断掉的情况。
Input示例
5 5 2 -1 3 3 0 6 1 -1 3 1 0 3 2 3
Output示例
3
思路:
1、首先我们知道,挂的绳子越多,越有可能爆炸,那么对应我们这里就有一个单调性,我们可以二分枚举一个当前值mid进行判断,如果可行,那么对应我们就要判断更多的绳子是否可行,否则就判断少一些的绳子是否可行,对于绳子数量,我们进行一次整体二分。
2、对应枚举出来的当前值mid,我们建立其mid条有向边,对应我们将0设定为根节点,连接钩子的绳子我们都将其连接到根节点上,对应第i条绳子尾部的节点我们都设定为编号i即可。
建好图之后,我们设定一个数组sum【i】,表示i号节点的总承重量,那么接下来我们从根节点开始Dfs.统计各个点的承重量即可。
接下来对每个节点的承重量进行判断,如果超额了,那么对应当前情况就是不可行的,否则就是可行的。
3、数据范围比较大,在统计sum的过程中会爆int,所以数据范围要注意一下。
Ac代码:
#include<stdio.h>
#include<string.h>
using namespace std;
#define ll __int64
struct node
{
int from;
int to;
int w;
int next;
}e[500006];
int degree[50006];
ll c[50006];
ll w[50006];
ll sum[500006];
int p[50006];
int vis[50006];
int head[50006];
int n,cont,flag;
void add(int from,int to)
{
e[cont].to=to;
e[cont].next=head[from];
head[from]=cont++;
}
ll Dfs(int u)
{
sum[u]+=w[u];
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
sum[u]+=Dfs(v);
}
return sum[u];
}
int Slove(int mid)
{
cont=0;
memset(sum,0,sizeof(sum));
memset(head,-1,sizeof(head));
for(int i=1;i<=mid;i++)
{
int v;
if(p[i]==-1)v=0;
else v=p[i]+1;
add(v,i);
}
Dfs(0);
for(int i=1;i<=mid;i++)
{
if(sum[i]>c[i])return 0;
}
return 1;
}
int main()
{
while(~scanf("%d",&n))
{
memset(c,0,sizeof(c));
memset(w,0,sizeof(w));
memset(p,0,sizeof(p));
for(int i=1;i<=n;i++)
{
scanf("%I64d%I64d%d",&c[i],&w[i],&p[i]);
}
int l=1;
int r=n;
int ans=0;
while(r-l>=0)
{
int mid=(l+r)/2;
if(Slove(mid)==1)
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
printf("%d\n",ans);
}
}
最后再上一发时间榜第一名选手的代码(并查集写成这么精简真的值得学习):