题目大意: 求 n n n 个点的强连通竞赛图中的哈密顿回路的期望个数。
题解
答案需要求期望个数,那么只需要求出总的哈密顿回路个数除以强联通竞赛图个数即可。
设 s [ i ] s[i] s[i] 表示 i i i 个点的哈密顿回路总数,发现其实 s [ i ] s[i] s[i] 是很好算的。
s[i]的求解 因为这 i i i 个点需要强联通,也就是它们要形成至少一个环,那么为了形成这个环,我们需要用掉 i i i 条边,但是这 i i i 个点的顺序是不确定的,所以它们的排列数量就相当于它们的全排列数量,即 i ! i! i!,但是因为它们是个环,也就是说对于 a → b → c → a a\to b \to c \to a a→b→c→a 和 b → c → a → b b\to c \to a \to b b→c→a→b 来说,它们是一样的,所以方案数还要去重,也就是除以 i i i,即 i ! i \frac {i!}i ii!。
还没完,一张竞赛图中有 i ( i − 1 ) 2 \frac {i(i-1)} 2 2i(i−1) 条边,现在我们只用了 i i i 条,剩下还有 i ( i − 1 ) 2 − i \frac {i(i-1)} 2 -i 2i(i−1)−i 条边我们还没用,它们的方向可以随意,所以方案数是 2 i ( i − 1 ) 2 − i 2^{\frac {i(i-1)} 2 -i} 22i(i−1)−i。
所以,总的方案数是: s [ i ] = i ! i × 2 i ( i − 1 ) 2 − i s[i]=\frac {i!} i \times 2^{\frac {i(i-1)}{2} -i} s[i]=ii!×22i(i−1)−i。
剩下的问题是,如何求强联通竞赛图个数。
我们设 f [ i ] f[i] f[i] 为 i i i 个点的强联通竞赛图个数, g [ i ] g[i] g[i] 为 i i i 个点的竞赛图个数,显然 g [ i ] = 2 i ( i − 1 ) 2 g[i]=2^{\frac {i(i-1)} 2} g[i]=22i(i−1)。
我们需要求 f [ i ] f[i] f[i],但是发现难以用 g [ i ] g[i] g[i] 来表示 f [ i ] f[i] f[i],此时就要想到用 f [ i ] f[i] f[i] 来表示 g [ i ] g[i] g[i],这是多项式求逆的一大套路。
这就很简单了,枚举一个
j
j
j 表示这
i
i
i 个点中有
j
j
j 个点强联通,那么有:
g
[
i
]
=
∑
j
=
1
i
C
i
j
f
[
j
]
g
[
i
−
j
]
g
[
i
]
=
∑
j
=
1
i
i
!
j
!
(
i
−
j
)
!
f
[
j
]
g
[
i
−
j
]
g
[
i
]
=
i
!
×
∑
j
=
1
i
f
[
j
]
j
!
g
[
i
−
j
]
(
i
−
j
)
!
g
[
i
]
i
!
=
∑
j
=
1
i
f
[
j
]
j
!
g
[
i
−
j
]
(
i
−
j
)
!
\begin{aligned} g[i]&=\sum_{j=1}^i C_i^jf[j]g[i-j]\\ g[i]&=\sum_{j=1}^i \frac {i!} {j!(i-j)!} f[j]g[i-j]\\ g[i]&=i! \times \sum_{j=1}^i \frac {f[j]} {j!} \frac {g[i-j]} {(i-j)!}\\ \frac {g[i]} {i!}&=\sum_{j=1}^i \frac {f[j]} {j!} \frac {g[i-j]} {(i-j)!} \end{aligned}
g[i]g[i]g[i]i!g[i]=j=1∑iCijf[j]g[i−j]=j=1∑ij!(i−j)!i!f[j]g[i−j]=i!×j=1∑ij!f[j](i−j)!g[i−j]=j=1∑ij!f[j](i−j)!g[i−j]
解法1——分治FFT
到这里大家应该也都看出来这是个分治 F F T FFT FFT 的形式了,只需要把里面右边柿子里拆一个 f [ i ] f[i] f[i] 出来,然后移一下项就可以了。
这个做法不够优秀,虽然也能 A C AC AC,但这里不多讨论。
解法2——生成函数+多项式求逆
发现这是一个指数生成函数的形式,于是设
G
(
x
)
=
∑
i
=
0
g
[
i
]
i
!
,
F
(
x
)
=
∑
i
=
0
f
[
i
]
i
!
G(x)=\sum\limits_{i=0} \frac {g[i]} {i!},F(x)=\sum\limits_{i=0} \frac {f[i]} {i!}
G(x)=i=0∑i!g[i],F(x)=i=0∑i!f[i],带入得:
G
(
x
)
=
F
(
x
)
G
(
x
)
+
g
[
0
]
G
(
x
)
=
F
(
x
)
G
(
x
)
+
1
−
1
=
F
(
x
)
G
(
x
)
−
G
(
x
)
−
1
=
G
(
x
)
(
F
(
x
)
−
1
)
F
(
x
)
=
1
−
1
G
(
x
)
\begin{aligned} G(x)&=F(x)G(x)+g[0]\\ G(x)&=F(x)G(x)+1\\ -1&=F(x)G(x)-G(x)\\ -1&=G(x)(F(x)-1)\\ F(x)&=1-\frac 1 {G(x)}\\ \end{aligned}
G(x)G(x)−1−1F(x)=F(x)G(x)+g[0]=F(x)G(x)+1=F(x)G(x)−G(x)=G(x)(F(x)−1)=1−G(x)1
多项式求逆搞一搞即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 300010
#define ll long long
#define mod 998244353
int n;
ll ksm(ll x,ll y)
{
ll re=1,tot=x;
while(y)
{
if(y&1)re=re*tot%mod;
tot=tot*tot%mod;
y>>=1;
}
return re;
}
#define inv(x) ksm(x,mod-2)
ll fac[maxn],invfac[maxn];
ll G[maxn],invG[maxn],F[maxn];
void work()
{
fac[0]=invfac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%mod,invfac[i]=inv(fac[i]);
G[0]=1;
for(int i=1;i<=n;i++)
G[i]=ksm(2ll,(ll)i*(i-1)/2ll)*invfac[i]%mod;
}
const int g=3,invg=inv(g);
int up,l,r[maxn];
void ntt(ll *f,int len,int type)
{
for(int i=1;i<len;i++)
if(i<r[i])swap(f[i],f[r[i]]);
for(int mid=1;mid<len;mid<<=1)
{
ll wn=ksm((type==1?g:invg),(mod-1)/mid/2);
for(int block=mid<<1,j=0;j<len;j+=block)
{
ll w=1;
for(int i=j;i<j+mid;i++,w=w*wn%mod)
{
ll x=f[i],y=f[i+mid]*w%mod;
f[i]=(x+y)%mod;f[i+mid]=(x-y+mod)%mod;
}
}
}
}
ll a[maxn],b[maxn];
void work(int len)
{
up=1;l=0;
while(up<=len)up<<=1,l++;
for(int i=1;i<up;i++)
r[i]=(r[i>>1]>>1)|((i&1)<<(l-1)),a[i]=b[i]=0;
}
void solve(int len,ll *arr,ll *invarr)
{
if(len==1){invarr[0]=inv(arr[0]);return;}
solve(len+1>>1,arr,invarr);
work(len+n);
for(int i=0;i<up;i++)a[i]=arr[i];
for(int i=0;i<(len+1>>1);i++)b[i]=invarr[i];
ntt(a,up,1);ntt(b,up,1);
for(int i=0;i<up;i++)
a[i]=b[i]*((2-a[i]*b[i]%mod+mod)%mod)%mod;
ntt(a,up,-1);
ll invup=inv(up);
for(int i=0;i<len;i++)
invarr[i]=a[i]*invup%mod;
}
void workF()
{
for(int i=0;i<=n;i++)
F[i]=(mod-invG[i])%mod;
F[0]=(F[0]+1)%mod;
for(int i=0;i<=n;i++)
F[i]=F[i]*fac[i]%mod;
}
int main()
{
scanf("%d",&n);
work();
solve(n+1,G,invG);
workF();
if(n>=1)printf("1\n");
if(n>=2)printf("-1\n");
for(int i=3;i<=n;i++)
printf("%lld\n",fac[i-1]*ksm(2,(ll)i*(i-1)/2ll-i)%mod*inv(F[i])%mod);
}