BZOJ3872: [Poi2014]Ant colony

111 篇文章 0 订阅
26 篇文章 0 订阅

题目大意:给一棵树,对于每个叶子节点,都有g群蚂蚁要从外面进来,每群蚂蚁在行进过程中只要碰到岔路,就将平均地分成岔路口数-1那么多份,然后平均地走向剩下的那些岔路口,余下的蚂蚁自动消失,树上有一个关键边,假如有一群蚂蚁通过了这条边且数量恰好为k,这k只蚂蚁就被吃掉,问一共有多少只蚂蚁被吃掉


显然得从那条关键边倒着推

以这条边作为这棵树的“根”,开始遍历其他的边,遍历到每条边的时候计算一下“到这条边时这群蚂蚁会被吃掉”的当时蚂蚁数量上限和下限

然后对于每个叶子节点的那些边,二分一下有多少组蚂蚁会被吃掉就好了


对了,这题有坑,需要到每条边的时候判断一下,这条边的下限是否已经比最大的那群蚂蚁大了,如果是的话就不继续往下算了,否则好像会乘起来爆longlong出负数之类的奇怪错误


#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 2000010
using namespace std;
long long m[N>>1];
long long to[N],nxt[N],pre[N>>1],cnt;
long long d[N>>1];
void ae(long long ff,long long tt)
{
	cnt++;
	to[cnt]=tt;
	nxt[cnt]=pre[ff];
	pre[ff]=cnt;
	d[ff]++;
}
long long mn[N>>1],mx[N>>1];
long long n,g,k;
void build(long long x,long long ff)
{
	long long i,j;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(j==ff) continue;
		mn[j]=mn[x]*(d[x]-1);
		mx[j]=mx[x]*(d[x]-1)+d[x]-2;
		mx[j]=min(mx[j],m[g]);
		if(mn[j]<=m[g])
		build(j,x);
	}
}
long long cal(long long x)
{
	long long L=1,R=g+1,mid;
	while(L<R)
	{
		mid=(L+R)>>1;
		if(m[mid]<=x) L=mid+1;
		else R=mid;
	}
	return L-1;
}
int main()
{
	scanf("%lld%lld%lld",&n,&g,&k);
	long long i,j,x,y;
	for(i=1;i<=g;i++)
	scanf("%lld",&m[i]);
	sort(m+1,m+g+1);
	long long s1,s2;
	scanf("%lld%lld",&s1,&s2);
	ae(s1,s2);ae(s2,s1);
	for(i=2;i<n;i++)
	{
		scanf("%lld%lld",&x,&y);
		ae(x,y);ae(y,x);
	}
	mn[s1]=mx[s1]=mx[s2]=mn[s2]=k;
	build(s1,s2);
	build(s2,s1);
	long long ans=0;
	for(i=1;i<=n;i++)
	if(d[i]==1)
	ans+=cal(mx[i])-cal(mn[i]-1);
	printf("%lld",ans*k);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值