[JSOI2019]神经网络
LebronDurant · · 题解
神仙题啊orz。树上背包+EGF。
考虑这个哈密顿回路可以拆分成什么。任意两棵树之间都可以到达,那么就可以拆分成一堆树的链的一个环,任意两个在环上相邻的链都不属于一棵树。首先求出每棵树拆分成
然后发现如果就是链排列的话,直接把这
第一棵树有一些特殊限定条件,先不考虑他。考虑其他树的EGF是什么。式子中的
首先拆分成
然后考虑第一棵树的限制怎么处理。第一个限制:第一条链必须属于第一棵树,且包含1号节点。那么就钦定第一棵树的包含1号节点的链是第一个即可。他不参与内部排列,不参与外部的有标号卷积,所以拆分成
第二个限制:最后一条链不可以属于第一棵树。那么上面的EGF是不考虑第二个限制的,把不符合第二个限制的部分减掉即可。也就是说我们钦定最后一条链属于第一棵树,把这个EGF减掉即可。最后一条链属于第一棵树的话,在这
假如说
真的是道非常好的题,认真思考会对EGF理解加深很多。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
#define N 300002
typedef long long ll;
const int p=998244353;
struct edge{int to,nxxt;}e[N<<1];
int m,k[302],head[N],cnt=1,dp[5002][5002][3],siz[N],t[5002][3],ct[5002];
int inv[N],fac[N],finv[N],f[N],g[N],py[N];
inline void ins(int u,int v){e[cnt].to=v;e[cnt].nxxt=head[u];head[u]=cnt++;}
void df5(int te,int la)
{siz[te]=1;dp[te][1][0]=1;
for(int i=head[te];i;i=e[i].nxxt)
{
int j=e[i].to;if(j==la)continue;
df5(j,te);
for(int ii=1;ii<=siz[te]+siz[j];ii++)t[ii][0]=t[ii][1]=t[ii][2]=0;
for(int ii=1;ii<=siz[te];ii++)for(int jj=1;jj<=siz[j];jj++)
{
int tdp=(1ll*dp[j][jj][0]+2ll*dp[j][jj][1]%p+dp[j][jj][2])%p;
(t[ii+jj][0]+=1ll*dp[te][ii][0]*tdp%p)%=p;
(t[ii+jj][1]+=1ll*dp[te][ii][1]*tdp%p)%=p;
(t[ii+jj][2]+=1ll*dp[te][ii][2]*tdp%p)%=p;
(t[ii+jj-1][1]+=1ll*dp[te][ii][0]*dp[j][jj][0]%p)%=p;
(t[ii+jj-1][1]+=1ll*dp[te][ii][0]*dp[j][jj][1]%p)%=p;
(t[ii+jj-1][2]+=2ll*dp[te][ii][1]*(dp[j][jj][0]+dp[j][jj][1])%p)%=p;
}siz[te]+=siz[j];
for(int ii=1;ii<=siz[te];ii++)
dp[te][ii][0]=t[ii][0],dp[te][ii][1]=t[ii][1],dp[te][ii][2]=t[ii][2];
}
}
inline int C(int nn,int mm)
{
if(nn<mm)return 0;
if(mm==0||nn==mm)return 1;
return 1ll*fac[nn]*finv[mm]%p*finv[nn-mm]%p;
}
int main()
{//freopen("7.in","r",stdin);
fac[0]=finv[0]=inv[1]=fac[1]=finv[1]=1;
for(int i=2;i<=100000;i++)
{
inv[i]=1ll*(p-p/i)*inv[p%i]%p;
fac[i]=1ll*fac[i-1]*i%p;finv[i]=1ll*finv[i-1]*inv[i]%p;
}
scanf("%d",&m);f[0]=1;int sum=0;
for(int i=1;i<=m;i++)
{
scanf("%d",&k[i]);
for(int j=1;j<=k[i];j++)head[j]=ct[j]=0;cnt=1;
for(int j=1;j<k[i];j++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y),ins(y,x);
}
for(int ii=1;ii<=k[i];ii++)for(int j=1;j<=k[i];j++)dp[ii][j][0]=dp[ii][j][1]=dp[ii][j][2]=0;
df5(1,1);for(int j=1;j<=k[i];j++)ct[j]=(1ll*dp[1][j][0]+2ll*dp[1][j][1]%p+dp[1][j][2])%p;
for(int j=0;j<=k[i];j++)g[j]=0;
//for(int j=1;j<=k[i];j++)printf("%d ",ct[j]);puts("");
if(i^1)
{
for(int ii=1;ii<=k[i];ii++)
for(int j=0;j<ii;j++)
{
if(j&1)g[ii-j]=(g[ii-j]-1ll*ct[ii]*fac[ii]%p*C(ii-1,j)%p+p)%p;
else g[ii-j]=(g[ii-j]+1ll*ct[ii]*fac[ii]%p*C(ii-1,j)%p)%p;
}
}
else
{
for(int ii=1;ii<=k[i];ii++)for(int j=0;j<ii;j++)
{
if(j&1)g[ii-j-1]=(g[ii-j-1]-1ll*ct[ii]*fac[ii-1]%p*C(ii-1,j)%p+p)%p;
else g[ii-j-1]=(g[ii-j-1]+1ll*ct[ii]*fac[ii-1]%p*C(ii-1,j)%p)%p;
}
for(int ii=1;ii<=k[i];ii++)for(int j=0;j<ii-1;j++)
{
if(j&1)g[ii-j-2]=(g[ii-j-2]+1ll*ct[ii]*fac[ii-1]%p*C(ii-1,j)%p)%p;
else g[ii-j-2]=(g[ii-j-2]-1ll*ct[ii]*fac[ii-1]%p*C(ii-1,j)%p+p)%p;
}
}
for(int ii=0;ii<=k[i];ii++)g[ii]=1ll*g[ii]*finv[ii]%p;
for(int ii=0;ii<=sum+k[i];ii++)py[ii]=0;
for(int ii=0;ii<=sum;ii++)for(int jj=0;jj<=k[i];jj++)
(py[ii+jj]+=1ll*f[ii]*g[jj]%p)%=p;
sum+=k[i];
for(int ii=0;ii<=sum;ii++)f[ii]=py[ii];
}
int ans=0;
for(int ii=0;ii<=sum;ii++)ans=(ans+1ll*f[ii]*fac[ii]%p)%p;
printf("%d\n",ans);
}
/*
3
4
2 1
4 3
3 1
5
4 5
3 4
3 2
1 2
1
*/