LOJ3102. 「JSOI2019」神经网络

\(m\)棵树,如果两个点不在一棵树内,那么它们之间要连一条边。

问哈密顿回路的个数。

\(n=\sum n_i\le 5000,m\le 300\)


简单计数然而作为计数白痴还是没有独立做出。

首先对每棵树求链剖分的方案数。\(c_{i,j}\)表示第\(i\)棵树,剖成\(j\)段的方案数。这个很好DP预处理,时间\(O(n^2)\)

接下来的问题是,有若干个球,颜色为\(i\)的有\(a_i\)个(且本质不同),求相邻颜色不同的圆排列方案数。

让我胡一个究极劣的做法:一个个颜色插入,设\(f_{i,j}\)表示考虑了前\(i\)个颜色,现在有\(j\)个位置相邻颜色相同。转移的时候枚举\(k\)表示\(a_{i+1}\)分成多少段,\(t\)表示多少段插入了之前相邻颜色相同的段中。再加上需要枚举\(a_i\)是什么,时间大概是\(O(n^6)\)级别……

直接dp没前途,考虑容斥。首先,固定颜色\(1\)的第一个点在开头,要求结尾颜色不为\(1\)(容斥掉)。

首先每个颜色内部都要排列,所以要乘上\((a_1-1)!\prod_{i=2}^m a_i!\)

容斥的时候,枚举分成了\(b_i\)段,容斥系数为\((-1)^{b_i-k}\),另外乘上分段的方案数\(\binom{a_i-1}{b_i-1}\)。容斥之后直接算方案数\(\frac{((\sum b_i)-1)!}{b_1!\prod_{i=2}^m b_i!}\)

对每个颜色搞个多项式\(F_k(x)\),卷起来。\(x\)的系数表示\((\sum b_i)-1\)

时间\(O(n^2)\)


using namespace std;
#include <bits/stdc++.h>
#define N 5005
#define M 305
#define ll long long
#define mo 998244353
ll qpow(ll x,ll y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;	
}
ll fac[N*2],ifac[N*2];
void initC(int n=10000){
	fac[0]=1;
	for (int i=1;i<=n;++i)
		fac[i]=fac[i-1]*i%mo;
	ifac[n]=qpow(fac[n]);
	for (int i=n-1;i>=0;--i)
		ifac[i]=ifac[i+1]*(i+1)%mo;
}
ll C(int m,int n){return fac[m]*ifac[n]%mo*ifac[m-n]%mo;}
int m;
struct EDGE{
	int to;
	EDGE *las;
};
ll c[N];
int mx[N];
ll dp[N][N][3],dp_[N][3];
ll inv2=qpow(2);
struct Graph{
	int n;
	EDGE e[N*2];
	int ne;
	EDGE *last[N];
	void link(int u,int v){
		e[ne]={v,last[u]};
		last[u]=e+ne++;
	}
	void dfs(int x,int fa){
		mx[x]=1;
		dp[x][1][0]=1,dp[x][1][1]=dp[x][1][2]=0;
		for (EDGE *ei=last[x];ei;ei=ei->las)
			if (ei->to!=fa){
				dfs(ei->to,x);
				int y=ei->to;
				memset(dp_,0,sizeof(ll)*3*(mx[x]+mx[y]+1));
				for (int i=1;i<=mx[x];++i)
					for (int j=1;j<=mx[y];++j){
						ll sum=dp[y][j][0]+dp[y][j][1]+dp[y][j][2];
						(dp_[i+j][0]+=dp[x][i][0]*sum)%=mo;
						(dp_[i+j][1]+=dp[x][i][1]*sum)%=mo;
						(dp_[i+j][2]+=dp[x][i][2]*sum)%=mo;
						(dp_[i+j-1][1]+=dp[x][i][0]*(dp[y][j][0]*2+dp[y][j][1]))%=mo;
						(dp_[i+j-1][2]+=dp[x][i][1]*(dp[y][j][0]+dp[y][j][1]*inv2%mo))%=mo;//pay attention to "inv2"
					}
				mx[x]+=mx[y];
				memcpy(dp[x],dp_,sizeof(ll)*3*(mx[x]+1));
			}
	}
	void calc(){
		dfs(1,0);
		for (int i=1;i<=n;++i)
			c[i]=(dp[1][i][0]+dp[1][i][1]+dp[1][i][2])%mo;
	}
} T[M];
ll f[N],nf,g[N],ng;
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	initC();
	scanf("%d",&m);
	for (int i=1;i<=m;++i){
		scanf("%d",&T[i].n);
		for (int j=1;j<T[i].n;++j){
			int u,v;
			scanf("%d%d",&u,&v);
			T[i].link(u,v);
			T[i].link(v,u);
		}
	}
	if (m==1){
		T[1].calc();
		printf("%lld\n",c[1]);
		return 0;
	}
	T[1].calc();
	for (int w=1;w<=T[1].n;++w){
		for (int i=1;i<=w;++i)
			(f[i-1]+=(w-i&1?-1:1)*C(w-1,i-1)*fac[w-1]%mo*ifac[i-1]%mo*c[w])%=mo;
		for (int i=2;i<=w;++i)
			(f[i-2]+=-(w-i&1?-1:1)*C(w-1,i-1)*fac[w-1]%mo*ifac[i-2]%mo*c[w])%=mo;
	}
	nf=T[1].n;
	for (int k=2;k<=m;++k){
		T[k].calc();
		ng=T[k].n;
		memset(g,0,sizeof(ll)*(1+ng));
		for (int w=1;w<=T[k].n;++w)
			for (int i=1;i<=w;++i)
				(g[i]+=(w-i&1?-1:1)*C(w-1,i-1)*fac[w]%mo*ifac[i]%mo*c[w])%=mo;
		static ll t[N];
		memset(t,0,sizeof(ll)*(1+nf+ng));
		for (int i=0;i<=nf;++i)
			for (int j=0;j<=ng;++j)
				(t[i+j]+=f[i]*g[j])%=mo;
		nf+=ng;
		memcpy(f,t,sizeof(ll)*(1+nf));
	}
	ll ans=0;
	for (int i=0;i<=nf;++i)
		(ans+=f[i]*fac[i])%=mo;
	ans=(ans+mo)%mo;
	printf("%lld\n",ans);
	return 0;
}

posted @ 2021-04-01 21:18  jz_597  阅读(75)  评论(0编辑  收藏  举报