题目大意:给一棵树,对于每个叶子节点,都有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);
}