[JSOI2019] 神经网络

一、题目

点此看题

二、解法

有一个神奇的题意转化:我们把每一棵树划分成若干条链,因为不同的树任意两点之间都有边,所以我们把这些链任意连接就形成哈密顿回路,要求是相邻的链必须来自不同的树。

首先我们考察把树划分成 \(i\) 条链的方案数 \(f_i\),可以直接树背包,在确定一条链并且这条链非单点的时候乘上 \(2\) 的系数。

然后考虑怎么把链任意连接,可以写出以链数为标记的 \(\tt EGF\),那么答案的 \(\tt EGF\) 就是所有树 \(\tt EGF\) 的卷积。由于第一棵树是不一样的,我们先考虑写出其他树的 \(\tt EGF\)

\[\sum_{i=1}^n f_i\cdot i!\sum_{j=1}^i(-1)^{i-j}\cdot {i-1\choose i-j}\cdot \frac{x^j}{j!} \]

首先是把这些链任意排列的方案数,即 \(f_i\cdot i!\);由于还有相邻链必须来自不同树的限制,所以要容斥。我们枚举最后形成了 \(j\) 条大链(相邻的同色链缩合在一起),那么就有 \(i-j\) 个空隙需要拼合,方案数是 \({i-1\choose i-j}\),容斥系数是 \((-1)^{i-j}\)

考虑写出第一棵树的 \(\tt EGF\),额外的限制是:起点必须是 \(1\) 号点。那么我们把 \(1\) 号点所在的链放在首位就行了,并且让它不参与和其他树 \(\tt EGF\) 的合并:

\[\sum_{i=1}^n f_i\cdot (i-1)!\sum_{j=1}^i(-1)^{i-j}\cdot {i-1\choose i-j}\cdot \frac{x^{j-1}}{(j-1)!} \]

还有一个限制是:最后一条链不能是第一棵树的链,那么我们减去这种情况即可,可以强制一条链放在最后,让它不参与和其他树 \(\tt EGF\) 的合并,所以要减去这样的 \(\tt EGF\)

\[\sum_{i=1}^nf_i\cdot (i-1)!\sum_{j=2}^i (-1)^{i-j}\cdot{i-1\choose i-j}\cdot\frac{x^{j-2}}{(j-2)!} \]

暴力实现卷积,时间复杂度 \(O(n^2)\)

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 5005;
const int MOD = 998244353;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int T,n,m,siz[M],dp[M][M][2],t[M][3];
int ans,fac[M],inv[M],f[M],g[M],z[M];
vector<int> e[M];
void add(int &x,int y) {x=(x+y)%MOD;}
void init(int n)
{
	fac[0]=inv[0]=inv[1]=1;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
	for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=2;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
}
int C(int n,int m)
{
	if(n<m || m<0) return 0;
	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
void dfs(int u,int fa)
{
	dp[u][0][0]=siz[u]=1;
	for(int v:e[u]) if(v^fa) dfs(v,u);
	for(int v:e[u]) if(v^fa)
	{
		for(int i=0;i<siz[u];i++)
			for(int j=1;j<=siz[v];j++)
			{
				add(t[i+j][0],dp[u][i][0]*dp[v][j][0]);
				add(t[i+j][1],dp[u][i][1]*dp[v][j][0]);
				add(t[i+j][1],dp[u][i][0]*dp[v][j][1]);
				add(t[i+j-1][2],dp[u][i][1]*dp[v][j][1]*2);
				add(t[i+j][2],f[i]*dp[v][j][0]);
			}
		siz[u]+=siz[v];
		for(int i=0;i<siz[u];i++)
		{
			dp[u][i][0]=t[i][0];
			dp[u][i][1]=t[i][1];
			f[i]=t[i][2];
			t[i][0]=t[i][1]=t[i][2]=0;
		}
		for(int j=0;j<=siz[v];j++)
			dp[v][j][0]=dp[v][j][1]=0;
	}
	for(int i=siz[u];i>=1;i--)
	{
		dp[u][i][0]=(dp[u][i-1][0]+f[i]+dp[u][i][1]*2)%MOD;
		dp[u][i][1]=(dp[u][i-1][0]+dp[u][i][1])%MOD;
		f[i]=0;
	}
	e[u].clear();
}
void work()
{
	n=read();static int start=1;
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++) f[i]=0;
	if(start)
	{
		for(int i=1;i<=n;i++)
			for(int j=1;j<=i;j++)
			{
				int t=dp[1][i][0]*fac[i-1]%MOD*C(i-1,i-j)
				%MOD*inv[j-1]%MOD;
				if((i-j)&1) add(f[j-1],MOD-t);
				else add(f[j-1],t);
				if(j>1)
				{
					t=t*(j-1)%MOD;
					if((i-j)&1) add(f[j-2],t);
					else add(f[j-2],MOD-t);
				}
			}
		start=0;
	}
	else
	{
		for(int i=1;i<=n;i++)
			for(int j=1;j<=i;j++)
			{
				int t=dp[1][i][0]*fac[i]%MOD*C(i-1,i-j)
				%MOD*inv[j]%MOD;
				if((i-j)&1) add(f[j],MOD-t);
				else add(f[j],t);
			}
	}
	for(int i=0;i<=m;i++)
		for(int j=0;j<=n;j++)
			add(z[i+j],f[j]*g[i]);
	m+=n;
	for(int i=0;i<=m;i++)
		g[i]=z[i],z[i]=0;
	for(int i=0;i<=n;i++)
		dp[1][i][0]=dp[1][i][1]=f[i]=0;
}
signed main()
{
	T=read();init(5000);g[0]=1;
	while(T--) work();
	for(int i=0;i<=m;i++) add(ans,g[i]*fac[i]);
	printf("%lld\n",ans);
}
posted @ 2022-06-13 15:53  C202044zxy  阅读(145)  评论(0编辑  收藏  举报