【2020 Multi-University 4 I】Imperative Meeting 题解

题目大意

  有一棵 n n n 个结点的树,现有 m m m 个人位于不同的结点,那么要让他们在同一结点相遇的话会有一个最小总路程。而“ m m m个人位于不同结点”共有 ( n m ) \binom nm (mn) 种情况,求这 ( n m ) \binom{n}{m} (mn) 种情况的最小总路程之和,模 1 0 9 + 7 10^9+7 109+7

   m ≤ n ≤ 1 0 6 m \le n \le 10^6 mn106
  多测, T ≤ 1000 T \le 1000 T1000 ∑ n ≤ 2 × 1 0 6 \sum n \le 2\times 10^6 n2×106
  2s

\\
\\
\\

题解

  考虑每条边的贡献,一条边有贡献当且仅当这条边两侧都有人,且贡献等于两侧人数的较小值。形式化地说,假设一条边连接了 x x x 子树和 y y y 子树,那么这条边的贡献为:
∑ i = 1 m − 1 ( s i z e x i ) ( s i z e y m − i ) min ⁡ ( i , m − i ) \sum_{i=1}^{m-1} \binom{size_x}{i} \binom{size_y}{m-i} \min(i,m-i) i=1m1(isizex)(misizey)min(i,mi)

  把 min ⁡ \min min 拆掉变成
∑ i = 1 ⌊ m − 1 2 ⌋ i ( s i z e x i ) ( s i z e y m − i ) + ∑ i = 1 ⌊ m − 1 2 ⌋ i ( s i z e y i ) ( s i z e x m − i ) + [ m 为 偶 数 ] m 2 ( s i z e x m 2 ) ( s i z e y m 2 ) \sum_{i=1}^{\lfloor \frac{m-1}{2} \rfloor} i\binom{size_x}{i} \binom{size_y}{m-i} + \sum_{i=1}^{\lfloor \frac{m-1}{2} \rfloor}i\binom{size_y}{i} \binom{size_x}{m-i} + [m为偶数]\frac m2 \binom{size_x}{\frac m2} \binom{size_y}{\frac m2} i=12m1i(isizex)(misizey)+i=12m1i(isizey)(misizex)+[m]2m(2msizex)(2msizey)

  这式子共 3 项,最后一项对于每条边单独算一下就行了,所以要算前两项,以第一项为例做一个变形把乘 i i i 去掉:
∑ i = 1 ⌊ m − 1 2 ⌋ i ( s i z e x i ) ( s i z e y m − i ) = ∑ i = 1 ⌊ m − 1 2 ⌋ i ⋅ s i z e x ! i ! ⋅ ( s i z e x − i ) ! ( s i z e y m − i ) = ∑ i = 1 ⌊ m − 1 2 ⌋ s i z e x ⋅ ( s i z e x − 1 ) ! ( i − 1 ) ! ⋅ ( s i z e x − i ) ! ( s i z e y m − i ) = s i z e x ∑ i = 1 ⌊ m − 1 2 ⌋ ( s i z e x − 1 i − 1 ) ( s i z e y m − i ) \begin{aligned} &\sum_{i=1}^{\lfloor \frac{m-1}{2} \rfloor} i\binom{size_x}{i} \binom{size_y}{m-i} \\ =& \sum_{i=1}^{\lfloor \frac{m-1}{2} \rfloor} i\cdot\frac{size_x!}{i!\cdot(size_x-i)!}\binom{size_y}{m-i} \\ =& \sum_{i=1}^{\lfloor \frac{m-1}{2} \rfloor} \frac{size_x \cdot (size_x-1)!}{(i-1)!\cdot(size_x-i)!}\binom{size_y}{m-i} \\ =& size_x\sum_{i=1}^{\lfloor \frac{m-1}{2} \rfloor} \binom{size_x-1}{i-1} \binom{size_y}{m-i} \end{aligned} ===i=12m1i(isizex)(misizey)i=12m1ii!(sizexi)!sizex!(misizey)i=12m1(i1)!(sizexi)!sizex(sizex1)!(misizey)sizexi=12m1(i1sizex1)(misizey)

  注意到 s i z e x + s i z e y = n size_x+size_y=n sizex+sizey=n,记 h s = ∑ i = 1 ⌊ m − 1 2 ⌋ ( s − 1 i − 1 ) ( n − s m − i ) h_s=\sum_{i=1}^{\lfloor \frac{m-1}{2} \rfloor} \binom{s-1}{i-1} \binom{n-s}{m-i} hs=i=12m1(i1s1)(mins),结果发现这玩意是能递推的!
  有两种方法能够得到递推结果。
  一种是考虑 h s h_s hs 的组合意义:把 m − 1 m-1 m1 个球放到 n − 1 n-1 n1 个盒子里,每个盒子最多放 1 1 1 个球,且要求前 s − 1 s-1 s1 个盒子最多只能有 ⌊ m − 1 2 ⌋ − 1 \lfloor \frac{m-1}{2} \rfloor-1 2m11 个球,问方案数。
   m − 1 m-1 m1 个球放入 n − 1 n-1 n1 个盒子共 ( n − 1 m − 1 ) \binom{n-1}{m-1} (m1n1) 种方案,这也是 h 1 h_1 h1 的值。随着 s s s 的增大,只会有越来越多的方案变得非法。从 h s − 1 h_{s-1} hs1 h s h_s hs 变得非法的方案,即为前 s − 2 s-2 s2 个盒子已经放够了 ⌊ m − 1 2 ⌋ − 1 \lfloor \frac{m-1}{2} \rfloor -1 2m11 个球,而第 s − 1 s-1 s1 个盒子又放了一个球,其余球放在后面的盒子里,方案数为 ( s − 2 ⌊ m − 1 2 ⌋ − 1 ) ( n − s m − 1 − ⌊ m − 1 2 ⌋ ) \binom{s-2}{\lfloor \frac{m-1}{2} \rfloor -1}\binom{n-s}{m-1-\lfloor \frac{m-1}{2} \rfloor} (2m11s2)(m12m1ns)
  另一种方法是硬推:
h s + 1 − h s = ∑ i = 1 ⌊ m − 1 2 ⌋ ( s i − 1 ) ( n − s − 1 m − i ) − ( s − 1 i − 1 ) ( n − s m − i ) h_{s+1}-h_s = \sum_{i=1}^{\lfloor \frac{m-1}{2} \rfloor} \binom{s}{i-1}\binom{n-s-1}{m-i}-\binom{s-1}{i-1}\binom{n-s}{m-i} hs+1hs=i=12m1(i1s)(mins1)(i1s1)(mins)

  把 i = 1 i=1 i=1 单独拉出来,其余的把 ( s i − 1 ) \binom{s}{i-1} (i1s) 拆成 ( s − 1 i − 1 ) + ( s − 1 i − 2 ) \binom{s-1}{i-1}+\binom{s-1}{i-2} (i1s1)+(i2s1)、把 ( n − s m − i ) \binom{n-s}{m-i} (mins) 拆成 ( n − s − 1 m − i ) + ( n − s − 1 m − i − 1 ) \binom{n-s-1}{m-i}+\binom{n-s-1}{m-i-1} (mins1)+(mi1ns1),然后基本上所有的项都被消去了,能得到同样的结果。式子太长就不写了。

  于是, O ( n ) O(n) O(n) 递推出 h h h,然后每条边单独算贡献,就得到答案了。

代码

#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long LL;

const int maxn=1e6+5;
const LL mo=1e9+7;

int n,m,mh;
vector<int> e[maxn];

LL Pow(LL x,LL y)
{
	LL re=1;
	for(; y; y>>=1, x=x*x%mo) if (y&1) re=re*x%mo;
	return re;
}

LL fac[maxn],inv[maxn];
void C_Pre(int n)
{
	fac[0]=1;
	fo(i,1,n) fac[i]=fac[i-1]*i%mo;
	inv[n]=Pow(fac[n],mo-2);
	fd(i,n-1,0) inv[i]=inv[i+1]*(i+1)%mo;
}
LL C(int n,int m) {return (n>=m && m>=0) ?fac[n]*inv[m]%mo*inv[n-m]%mo :0 ;}

void ReadInt(int &data)
{
	data=0;
	char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	do{
		data=(data<<3)+(data<<1)+ch-'0';
		ch=getchar();
	} while (ch>='0' && ch<='9');
}

LL h[maxn];
void Pre()
{
	int mhv=(m-1)>>1;
	if (mhv==0)
	{
		fo(i,1,n-1) h[i]=0;
		return;
	}
	h[1]=C(n-1,m-1);
	fo(i,2,n-1) h[i]=(h[i-1]-C(i-2,mhv-1)*C(n-i,m-1-mhv)%mo+mo)%mo;
	fo(i,1,n-1) (h[i]*=i)%=mo;
}

int size[maxn];
void dfs_size(int k,int last)
{
	size[k]=1;
	for(int son:e[k]) if (son!=last)
	{
		dfs_size(son,k);
		size[k]+=size[son];
	}
}

LL ans;
void dfs_ans(int k,int last)
{
	for(int son:e[k]) if (son!=last)
	{
		(ans+=h[size[son]]+h[n-size[son]]+((m&1) ?0 :C(size[son],mh)*C(n-size[son],mh)%mo*mh ))%=mo;
		dfs_ans(son,k);
	}
}

int T;
int main()
{
	C_Pre(1e6);
	
	scanf("%d",&T);
	while (T--)
	{
		ReadInt(n), ReadInt(m);
		mh=m>>1;
		fo(i,1,n) e[i].clear();
		fo(i,2,n)
		{
			int x;
			ReadInt(x);
			e[x].push_back(i);
		}
		
		Pre();
		dfs_size(1,0);
		ans=0;
		dfs_ans(1,0);
		
		printf("%lld\n",ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值