神经网络

经典的小球模型。
显然状压是过不了后面的点的。
考虑挖掘哈密尔顿回路的性质,它的性质是:在遍历某棵树时,在这棵树上走过的轨迹必须是一条链,遍历完之后要跳到另一颗树。
所以哈密尔顿回路实际上由若干条链组成。
设链\(i\)在树\(x\)中,则相邻的链不能在同一棵树。
这是个经典的问题(小球容斥模型)。
先考虑求出\(f_{i,j}\)表示树\(i\)被划分成\(j\)条链的方案数。
这可以通过dp求出。
\(g_{i,j,k}\)表示\(i\)子树中选出\(j\)条链,\(i\)向子树连出\(k\)条边。
可以树形背包求。
注意到对于长度\(>1\)的链(设开头结尾为\(a,b\)),进入/出它的方案有\(2\)类:\(a\)\(b\)出,\(b\)\(a\)出,所以答案要\(*=2\)
先考虑序列问题。
考虑求出每棵树的egf(就是每种"链小球"的egf)。设树被划分为\(k\)条链。
公式:\(=f_{i,k} \sum\limits_{j=1}^k (-1)^{(k-j)} \binom{k-1}{j-1} \frac{x^j}{j!}\)
原因:\(f_{i,k}\)就是选出\(k\)条链的方案。
考虑容斥。容斥有多少条链是必须接在一起的。
这些链可以被缩成一个小球。
\((-1)^{(k-j)}\)表示容斥系数。
如果某些链被接在一起且它们相邻,把这两条链(小球)之间连一条边。
\(\binom{k-1}{j-1}\)表示在当前链序列中选择\(j\)个空位,插入边的方案。
对于每个\(k\)求和就是整棵树的生成函数。
但是还要考虑处理环的问题。
考虑钦定一颗树,使第一条链一定来自这棵树。
由于第一条链必须在第一位,生成函数为\(\sum\limits_{k=1}^{k_i} \frac{f_{i,k}}{k} \sum\limits_{j=1}^k (-1)^{(k-j)} \binom{k-1}{j-1} \frac{x^{j-1}}{(j-1)!}\)
前面要除以\(k\)是因为当前树可能有\(k\)条链作为起始位置,导致算重\(k\)次。
但是发现第一棵树不能有相邻的两条链,所以第一颗树的生成函数要减去\(\sum\limits_{k=1}^{k_i} \frac{f_{i,k}}{k} \sum\limits_{j=1}^k (-1)^{(k-j)} \binom{k-1}{j-1} \frac{x^{j-2}}{(j-2)!}\)
最后把所有树的生成函数卷积(暴力即可)就是答案。
时间复杂度还是\(O(n^2)\)
证明:设我们要卷积的多项式大小为\(a_1,a_2...a_k\)
构造一颗树:把\(a_i\)裂成\(i\)个点连成链,然后把\(a_{i-1}\)的链尾连向\(a_i\)的链头。
如果倒着进行卷积,时间复杂度一定低于于在这条链上进行树形背包的时间复杂度。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mo 998244353
#define N 5010
int t[N],jc[N],ij[N],iv[N],h[N],v[N*2],nxt[N*2],ec,m,sz[N],ans[N],va,tp[N][3],sc[N],f[N][N][3],c[N],i2;
int cc(int y,int x){
	if(y<0||x<0||y<x)
		return 0;
	return jc[y]*ij[x]%mo*ij[y-x]%mo;
}
void add(int x,int y){
	v[++ec]=y;
	nxt[ec]=h[x];
	h[x]=ec;
}
void ml(int *a,int *b,int *c,int d,int e){
	for(int i=0;i<=d+e;i++)
		t[i]=0;
	for(int i=0;i<=d;i++)
		for(int j=0;j<=e;j++)
			t[i+j]=(t[i+j]+a[i]*b[j]%mo)%mo;
	for(int i=0;i<=d+e;i++)
		c[i]=t[i];
}
int qp(int x,int y){
	int r=1;
	for(;y;y>>=1,x=x*x%mo)
		if(y&1)
			r=r*x%mo;
	return r;
}
void dfs(int x,int fa){
	sz[x]=f[x][1][0]=1;
	for(int i=h[x];i;i=nxt[i])
		if(v[i]!=fa){
			int y=v[i];
			dfs(y,x);
			for(int j=1;j<=sz[y];j++){
				int tv=(f[y][j][0]+f[y][j][1]+f[y][j][2])%mo;
				for(int k=1;k<=sz[x];k++){
					tp[j+k][0]=(tp[j+k][0]+tv*f[x][k][0]%mo)%mo;
					tp[j+k][1]=(tp[j+k][1]+tv*f[x][k][1]%mo)%mo;
					tp[j+k][2]=(tp[j+k][2]+tv*f[x][k][2]%mo)%mo;
					tp[j+k-1][1]=(tp[j+k-1][1]+f[x][k][0]*(f[y][j][1]+2*f[y][j][0]%mo)%mo)%mo;
					tp[j+k-1][2]=(tp[j+k-1][2]+f[x][k][1]*(i2*f[y][j][1]%mo+f[y][j][0])%mo)%mo;
				}
			}
			sz[x]+=sz[y];
			for(int j=0;j<=sz[x];j++){
				f[x][j][0]=tp[j][0];
				f[x][j][1]=tp[j][1];
				f[x][j][2]=tp[j][2];
				tp[j][0]=tp[j][1]=tp[j][2]=0;
			}
		}
}
signed main(){
	i2=(mo+1)/2;
	jc[0]=ij[0]=1;
	for(int i=1;i<N;i++){
		jc[i]=jc[i-1]*i%mo;
		ij[i]=qp(jc[i],mo-2);
		iv[i]=qp(i,mo-2);
	}
	int sn=0;
	ans[0]=1;
	scanf("%lld",&m);
	for(int i=m;i;i--){
		int n;
		scanf("%lld",&n);
		for(int j=1;j<n;j++){
			int x,y;
			scanf("%lld%lld",&x,&y);
			add(x,y);
			add(y,x);
		}
		dfs(1,0);
		for(int j=1;j<=n;j++)
			c[j]=(f[1][j][0]+f[1][j][1]+f[1][j][2])%mo*jc[j]%mo;
		for(int j=1;j<=n;j++){
			int va=0;
			for(int k=j;k<=n;k++){
				int bp=1,ii=1;
				if(i==1)
					ii=iv[k];
				if((k-j)&1)
					bp=mo-1;
				va=(va+bp*c[k]%mo*cc(k-1,j-1)%mo*ii%mo+mo)%mo;
			}
			if(i==1){
				sc[j-1]=va;
				if(j>=2)
					sc[j-2]=(sc[j-2]-va+mo)%mo;
			}
			else
				sc[j]=va;
		}
		for(int j=0;j<=n;j++)
			sc[j]=sc[j]*ij[j]%mo;
		ml(ans,sc,ans,sn,n);
		sn+=n;
		for(int j=1;j<=ec;j++)
			nxt[j]=v[j]=0;
		for(int j=1;j<=n;j++){
			h[j]=sc[j]=c[j]=0;
			for(int k=1;k<=n;k++)
				f[j][k][0]=f[j][k][1]=f[j][k][2]=0;
		}
		ec=0;
	}
	for(int i=1;i<=sn;i++)
		va=(va+jc[i]*ans[i])%mo;
	printf("%lld",va);
}
posted @ 2021-02-24 15:48  celerity1  阅读(82)  评论(0)    收藏  举报