[ARC087F] Squirrel Migration 题解

如果两条路径不存在交点,则两条路径各选一个端点交换后两路径相交,答案不会变劣。

考虑所有路径两两相交的情况,因为图是一棵树,所以这些路径会交于一点。以这个点为根,那么最大的子树大小一定不超过 \(\frac n 2\),所以这个点是树的重心,每条路径的端点一定在两个不同的子树内。如果这个树存在两个重心,则以其中一个为根,显然另一个重心的子树大小一定为 \(\frac n 2\),所以每条路径都有一个端点在这个子树中,即所有路径共有两个交点,随便以一个重心为根计算答案即可。

考虑怎么计算答案,直接用背包做好像过不去,正难则反,考虑容斥。

\(f_i\) 表示钦定 \(i\) 个点满足这些点与对应 \(p\) 的路径在同一个子树内,这些点\(p\) 值有多少种可能。每次加入一个大小为 \(s\) 的子树时,易得转移:

\[f_i=\sum_{j=0}^{\min(i,s)} {s\choose j}^2\times j!\times{f'}_{i-j} \]

那么答案就是:

\[\sum_{i=0}^n (-1)^i\times (n-i)!\times f_i \]

然后这道题就做完了,时间复杂度 \(O(n^2)\)

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 5003
#define md 1000000007
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
using namespace std;
int n,rt,ans,sz[mxn],c[mxn][mxn],d[mxn],dp[2][mxn];
vector<int>s,g[mxn];
ll fac[mxn];
bool fl;
void dfs(int x,int fa){
	sz[x]=1;
	int mx=0;
	for(int i:g[x])if(i!=fa){
		dfs(i,x);
		mx=max(mx,sz[i]);
		sz[x]+=sz[i];
	}
	mx=max(mx,n-sz[x]);
	if(mx*2<=n)rt=x;
}
void dfs1(int x,int fa){
	sz[x]=1;
	for(int i:g[x])if(i!=fa){
		dfs1(i,x);
		sz[x]+=sz[i];
	}
}
void solve(){
	memset(dp,0,sizeof(dp));
	fl=0;
	dp[0][0]=1;
	int sz=1;
	for(int i:s){
		fl^=1;
		memset(dp[fl],0,sizeof(dp[fl]));
		rep(j,0,sz)if(dp[fl^1][j]){
			rep(k,0,i){
				dp[fl][j+k]=(dp[fl][j+k]+dp[fl^1][j]*fac[k]%md*c[i][k]%md*c[i][k])%md;
			}
		}
		sz+=i;
	}
	rep(i,0,sz){
		if(i&1)ans=(ans-dp[fl][i]*fac[sz-i])%md;
		else ans=(ans+dp[fl][i]*fac[sz-i])%md;
	}
}
signed main(){
	scanf("%d",&n);
	fac[0]=1;
	rep(i,1,n)fac[i]=fac[i-1]*i%md;
	c[0][0]=1;
	rep(i,1,n){
		c[i][0]=1;
		rep(j,1,i)c[i][j]=(c[i-1][j-1]+c[i-1][j])%md;
	}
	for(int i=1,x,y;i<n;++i){
		scanf("%d%d",&x,&y);
		g[x].pb(y),g[y].pb(x);
	}
	dfs(1,0);
	dfs1(rt,0);
	s.clear();
	for(int i:g[rt])s.pb(sz[i]);
	solve();
	cout<<(ans+md)%md;
	return 0;
}
posted @ 2023-12-02 13:01  zifanwang  阅读(6)  评论(0编辑  收藏  举报  来源