【洛谷4233】射命丸文的笔记(指数型生成函数+多项式求逆)
大致题意: 对于\(1\sim n\)之间的每个\(i\),求\(i\)个点带标号强连通竞赛图中哈密顿回路数目的平均值。
推式子
考虑平均值等于总和除以个数,因此我们只要分别求出哈密顿回路总数和强连通竞赛图总数即可。
对于哈密顿回路总数,由于一共有\((n-1)!\)种哈密顿回路,每一种哈密顿回路出现在\(2^{\frac{n(n-1)}2-n}\)张图中(即除了这条哈密顿回路上的边,其他边都可以任意定向),因此哈密顿回路总数为:
\[(n-1)!\times 2^{\frac{n(n-1)}2-n}
\]
然后我们要计算强连通竞赛图总数,则套路地设\(f(n)\)为有\(n\)个点的强连通竞赛图数目,\(g(n)\)为有\(n\)个点的竞赛图数目,显而易见有:
\[g(n)=2^{\frac{n(n-1)}2}
\]
对于任一竞赛图,我们可以去枚举拓扑序最小的强连通分量大小,得到:
\[g(n)=\sum_{i=1}^nC_n^i\times f(i)\times g(n-i)
\]
根据指数型生成函数的一般套路,拆开\(C_n^i\)分配给式子中的每一项,得到:
\[\frac {g(n)}{n!}=\sum_{i=1}^n\frac{f(i)}{i!}\times\frac{g(n-i)}{(n-i)!}
\]
令\(F(x),G(x)\)分别为\(\frac{f(i)}{i!}\)和\(\frac{g(i)}{i!}\)的生成函数,得到:(加\(1\)是因为\(g(0)=1\))
\[G(x)=F(x)*G(x)+1
\]
进一步移项并化简就可以得到:
\[F(x)=1-\frac1{G(x)}
\]
因此只要多项式求逆一下就可以求出\(F(x)\),而\(f(n)=n!\times F(n)\)。
最终对于每个\(i\),它的答案就是:
\[\frac{(i-1)!\times 2^{\frac{i(i-1)}2-i}}{i!\times F(i)}=\frac{2^{\frac{i(i-1)}2-i}}{i\times F(i)}
\]
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define X 998244353
using namespace std;
int n,f[N+5],g[N+5],g_[N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
namespace Poly
{
#define Init(n) P=1,L=0;W(P<=2*(n)) P<<=1,++L;\
for(i=0;i^P;++i) A[i]=B[i]=0,R[i]=(R[i>>1]>>1)|((i&1)<<L-1);//多项式卷积初始化
int PR=3,IPR=QP(3,X-2),P,L,R[4*N+5],A[4*N+5],B[4*N+5];
I void NTT(int *s,CI t)//NTT
{
RI i,j,k,x,y,U,S;for(i=0;i^P;++i) i<R[i]&&(x=s[i],s[i]=s[R[i]],s[R[i]]=x);
for(i=1;i^P;i<<=1) for(U=QP(t,(X-1)/(i<<1)),j=0;j^P;j+=i<<1) for(S=1,k=0;k^i;
S=1LL*S*U%X,++k) s[j+k]=((x=s[j+k])+(y=1LL*S*s[i+j+k]%X))%X,s[i+j+k]=(x-y+X)%X;
}
I void Inv(CI n,int *a,int *b)//多项式乘法逆
{
if(!n) return (void)(b[0]=1);RI i;Inv(n>>1,a,b);
Init(n);for(i=0;i<=n;++i) A[i]=a[i],B[i]=b[i];
for(NTT(A,PR),NTT(B,PR),i=0;i^P;++i) B[i]=(2LL*B[i]-1LL*A[i]*B[i]%X*B[i]%X+X)%X;
RI t=QP(P,X-2);for(NTT(B,IPR),i=0;i<=n;++i) b[i]=1LL*B[i]*t%X;
}
}
int main()
{
RI i,t,F=1;for(scanf("%d",&n),g[0]=i=1;i<=n;++i)
F=1LL*F*i%X,g[i]=1LL*QP(2,(1LL*i*(i-1)/2)%(X-1))*QP(F,X-2)%X;//预处理g
for(Poly::Inv(n,g,g_),i=0;i<=n;++i) f[i]=X-g_[i];++f[0]==X&&(f[0]=0);//多项式求逆,然后求出f
for(n>=1&&puts("1"),n>=2&&puts("-1"),i=3;i<=n;++i) printf("%d\n",//特殊处理n<=2的数据
1LL*QP(2,(1LL*i*(i-1)/2-i)%(X-1))%X*QP(1LL*f[i]*i%X,X-2)%X);return 0;//计算答案
}
待到再迷茫时回头望,所有脚印会发出光芒