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;
}