bzoj 3872: [Poi2014]Ant colony (dfs+二分)

41 篇文章 0 订阅

3872: [Poi2014]Ant colony

Time Limit: 30 Sec   Memory Limit: 128 MB
Submit: 167   Solved: 61
[ Submit][ Status][ Discuss]

Description

There is an entrance to the ant hill in every chamber with only one corridor leading into (or out of) it. At each entry, there are g groups of m1,m2,...,mg ants respectively. These groups will enter the ant hill one after another, each successive group entering once there are no ants inside. Inside the hill, the ants explore it in the following way:
Upon entering a chamber with d outgoing corridors yet unexplored by the group, the group divides into d groups of equal size. Each newly created group follows one of the d corridors. If d=0, then the group exits the ant hill.
If the ants cannot divide into equal groups, then the stronger ants eat the weaker until a perfect division is possible. Note that such a division is always possible since eventually the number of ants drops down to zero. Nothing can stop the ants from allowing divisibility - in particular, an ant can eat itself, and the last one remaining will do so if the group is smaller than d.
The following figure depicts m ants upon entering a chamber with three outgoing unexplored corridors, dividing themselves into three (equal) groups of floor(m/3) ants each.
A hungry anteater dug into one of the corridors and can now eat all the ants passing through it. However, just like the ants, the anteater is very picky when it comes to numbers. It will devour a passing group if and only if it consists of exactly k ants. We want to know how many ants the anteater will eat.
给定一棵有n个节点的树。在每个叶子节点,有g群蚂蚁要从外面进来,其中第i群有m[i]只蚂蚁。这些蚂蚁会相继进入树中,而且要保证每一时刻每个节点最多只有一群蚂蚁。这些蚂蚁会按以下方式前进:
·在即将离开某个度数为d+1的点时,该群蚂蚁有d个方向还没有走过,这群蚂蚁就会分裂成d群,每群数量都相等。如果d=0,那么蚂蚁会离开这棵树。
·如果蚂蚁不能等分,那么蚂蚁之间会互相吞噬,直到可以等分为止,即一群蚂蚁有m只,要分成d组,每组将会有floor(m/d)只,如下图。
一只饥饿的食蚁兽埋伏在一条边上,如果有一群蚂蚁通过这条边,并且数量恰为k只,它就会吞掉这群蚂蚁。请计算一共有多少只蚂蚁会被吞掉。

Input

The first line of the standard input contains three integers n, g, k (2<=n,g<=1000000, 1<=k<=10^9), separated by single spaces. These specify the number of chambers, the number of ant groups and the number of ants the anteater devours at once. The chambers are numbered from 1 to n.
The second line contains g integers m[1],m[2],...,m[g](1<=m[i]<=10^9), separated by single spaces, where m[i] gives the number of ants in the i-th group at every entrance to the ant hill. The n-1 lines that follow describe the corridors within the ant hill; the i-th such line contains two integers a[i],b[i] (1<=a[i],b[i]<=n), separated by a single space, that indicate that the chambers no.a[i] and b[i] are linked by a corridor. The anteater has dug into the corridor that appears first on input.
第一行包含三个整数n,g,k,表示点数、蚂蚁群数以及k。
第二行包含g个整数m[1],m[2],...,m[g],表示每群蚂蚁中蚂蚁的数量。
接下来n-1行每行两个整数,表示一条边,食蚁兽埋伏在输入的第一条边上。

Output

Your program should print to the standard output a single line containing a single integer: the number of ants eaten by the anteater.
一个整数,即食蚁兽能吃掉的蚂蚁的数量。

Sample Input

7 5 3
3 4 1 9 11
1 2
1 4
4 3
4 5
4 6
6 7

Sample Output

21

HINT

Source

[ Submit][ Status][ Discuss]

题解:dfs+二分。

每个叶子节点都会有m群蚂蚁,而且叶子节点有可能接近10^6,所以从叶子节点向第一条边推得方法是不可行的。

我们考虑从第一条边的两个节点开始推,以这条边为界,将其分成两颗树,分别向两侧推,因为是floor(m/d)

所以可行的一定是一段范围,所以我只要预处理出每个叶子节点在什么范围内,能对答案产生贡献即可。

然后在m群蚂蚁中,二分查找符合范围的区间即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 2000003
#define LL long long
using namespace std;
int n,m,sx,sy,tot;
LL a[N],k,l[N],r[N],ans;
int point[N],next[N],v[N],size[N];
void add(int x,int y)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;
	tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x;
	size[x]++; size[y]++;
	//cout<<x<<" "<<y<<endl;
}
void dfs(int x,int fa) 
{
	for (int i=point[x];i;i=next[i]) 
	 if (v[i]!=fa) {
	 	l[v[i]]=l[x]*(size[x]-1);
	 	if (l[v[i]]>a[m]) l[v[i]]=a[m]+1;
	 	if (size[x]>2)  r[v[i]]=(r[x]+1)*(size[x]-1)-1;
	 	else r[v[i]]=r[x];
	 	r[v[i]]=min(r[v[i]],a[m]);
	 	if (l[v[i]]<=r[v[i]])  dfs(v[i],x);
	 }
}
int solve(LL x)
{
	int l=1; int r=m; int ans=m;
	while (l<=r){
		int mid=(l+r)/2;
		if (a[mid]>=x)  ans=min(ans,mid),r=mid-1;
		else l=mid+1;
	}
	return ans;
}
int solve1(LL x) 
{
	int l=1; int r=m; int ans=1;
	while (l<=r) {
		int mid=(l+r)/2;
		if(a[mid]<=x) ans=max(ans,mid),l=mid+1;
		else r=mid-1;
	}
	return ans;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d%d%I64d",&n,&m,&k);
	for (int i=1;i<=m;i++) scanf("%I64d",&a[i]);
	sort(a+1,a+m+1);
	for (int i=1;i<n;i++) {
		int x,y; scanf("%d%d",&x,&y);
		if (i==1) sx=x,sy=y;
		add(x,y);
	} 
	l[sx]=l[sy]=k; r[sx]=r[sy]=k;
	dfs(sx,sy); dfs(sy,sx);
	for (int i=1;i<=n;i++) 
	if (size[i]==1){
	  	int x=solve(l[i]); 
	  	int y=solve1(r[i]);
	  	if (y<x||r[i]<a[1]||l[i]>a[m]) continue;
	  	ans+=(LL)(y-x+1)*k;
	}
	printf("%I64d\n",ans);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值