树上竞技[数学推导][Dp]

文章目录

题目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路

考试时候想了一个性质
首先算每条边的贡献
假设一条边一边点集大小为 s s s,在其中有 x x x 个人
显然有 m i n ( x , m − x ) min(x,m-x) min(x,mx) 个人经过
贡献为 ∑ x = 1 m m i n ( x , m − x ) ( s x ) ( n − s m − x ) \sum_{x=1}^mmin(x,m-x)\dbinom{s}{x}\dbinom{n-s}{m-x} x=1mmin(x,mx)(xs)(mxns)
发现有 m i n min min 不好做,考虑去掉 m i n min min
k = ⌊ m − 1 2 ⌋ k=\lfloor\frac{m-1}{2}\rfloor k=2m1
f ( s ) = ∑ x = 1 k x ( s x ) ( n − s m − x ) f(s)=\sum_{x=1}^kx\dbinom{s}{x}\dbinom{n-s}{m-x} f(s)=x=1kx(xs)(mxns)
m m m 是奇数时候贡献为 f ( s ) + f ( n − s ) f(s)+f(n-s) f(s)+f(ns)
m m m 是偶数时候贡献为 f ( s ) + f ( n − s ) + m / 2 ( s m / 2 ) ( n − s m / 2 ) f(s)+f(n-s)+m/2\dbinom{s}{m/2}\dbinom{n-s}{m/2} f(s)+f(ns)+m/2(m/2s)(m/2ns)

f ( s ) = ∑ x = 1 k x s ! x ! ( s − x ) ! ( n − s m − x ) = f ( s ) = s ∑ x = 1 k ( s − 1 ) ! ( x − 1 ) ! ( s − x ) ! ( n − s m − x ) f ( s ) = s ∑ x = 1 k ( s − 1 x − 1 ) ( n − s m − x ) = s ⋅ g ( s ) f(s)=\sum_{x=1}^kx\frac{s!}{x!(s-x)!}\dbinom{n-s}{m-x}=\\f(s)=s\sum_{x=1}^k\frac{(s-1)!}{(x-1)!(s-x)!}\dbinom{n-s}{m-x}\\ f(s)=s\sum_{x=1}^k\dbinom{s-1}{x-1}\dbinom{n-s}{m-x}=s\cdot g(s) f(s)=x=1kxx!(sx)!s!(mxns)=f(s)=sx=1k(x1)!(sx)!(s1)!(mxns)f(s)=sx=1k(x1s1)(mxns)=sg(s)
考虑 g ( s ) g(s) g(s) 的组合意义:
在长度为 n − 1 n-1 n1 的序列中选择 m − 1 m-1 m1 个数,并且要求在前 s − 1 s-1 s1 最多选 k − 1 k-1 k1
考虑递推,发现本质上只会算少选择第 s s s 个和在前 s − 1 s-1 s1 个选 k − 1 k-1 k1 个的方案数即后 [ s + 1 , n − 1 ] [s+1,n-1] [s+1,n1] 选择 m − k − 1 m-k-1 mk1 个:
g ( s + 1 ) = g ( s ) − ( s − 1 k − 1 ) ( n − s − 1 m − k − 1 ) g(s+1)=g(s)-\dbinom{s-1}{k-1}\dbinom{n-s-1}{m-k-1} g(s+1)=g(s)(k1s1)(mk1ns1)
注意 k = 0 k=0 k=0 时候 g ( 1 ) = 0 g(1)=0 g(1)=0
时间复杂度 O ( n ) O(n) O(n)

代码

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<climits>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
int read(){
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define mp make_pair
const int MAXN=1000000;
const int Mod=(int)(1e9+7);
int Mul(LL x,int y){x*=y;return x>=Mod?x%Mod:x;}
int Sub(int x,int y){x-=y;return x<0?x+Mod:x;}
int Add(int x,int y){x+=y;return x>=Mod?x-Mod:x;}
int fac[MAXN+5],inv[MAXN+5];
int Pow(int x,int y){
	int ret=1;
	while(y){
		if(y&1) ret=Mul(ret,x);
		x=Mul(x,x),y>>=1;
	}
	return ret;
}
int C(int n,int m){if(n<m||m<0) return 0;return Mul(fac[n],Mul(inv[m],inv[n-m]));}
int n,m,f[MAXN+5];
int ans,siz[MAXN+5];
struct Edge{
	int v,nxt;
}edge[2*MAXN+5];
int ecnt,head[MAXN+5];
void Addedge(int u,int v){
	edge[++ecnt]=(Edge){v,head[u]},head[u]=ecnt;
	edge[++ecnt]=(Edge){u,head[v]},head[v]=ecnt;
	return ;
}
void DFS(int u,int fa){
	siz[u]=1;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v;
		if(v==fa) continue;
		DFS(v,u),siz[u]+=siz[v];
		int s=siz[v];
		ans=Add(ans,Add(f[s],f[n-s]));
		if(!(m&1))
			ans=Add(ans,Mul(s,Mul(C(s-1,m/2-1),C(n-s,m/2))));
	}
	return ;
}
int main(){
	freopen("meeting.in","r",stdin);
	freopen("meeting.out","w",stdout);
	n=read(),m=read();
	for(int u=2;u<=n;u++)
		Addedge(u,read());
	fac[0]=1;
	for(int i=1;i<=n;i++)
		fac[i]=Mul(fac[i-1],i);
	inv[n]=Pow(fac[n],Mod-2);
	for(int i=n-1;i>=0;i--)
		inv[i]=Mul(inv[i+1],i+1);
	int k=(m-1)/2;
	if(!k) f[1]=0;
	else f[1]=C(n-1,m-1);
	for(int i=2;i<=n;i++)
		f[i]=Sub(f[i-1],Mul(C(i-2,k-1),C(n-i,m-k-1)));
	for(int i=2;i<=n;i++)
		f[i]=Mul(f[i],i);		
	DFS(1,0);
	printf("%d\n",ans);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值