luogu 4233 射命丸文的笔记 多项式求逆

题意

给出n,求对于任意的 1in 1 ≤ i ≤ n ,求在所有i个点且有哈密顿回路的竞赛图中,哈密顿回路的期望数量是多少。答案模998244353
n105 n ≤ 10 5

分析

所有竞赛图的哈密顿回路数量很好求,考虑每一条哈密顿路径的贡献,那么答案就是 (n1)!2n(n1)2n ( n − 1 ) ! ∗ 2 n ( n − 1 ) 2 − n
现在考虑如何求有哈密顿回路的竞赛图数量。
首先有个结论,就是一个竞赛图存在哈密顿回路当且仅当其是一个强连通图。
证明:若其不是强连通图则显然没有哈密顿回路。若其是强连通图,考虑归纳。假设在当n=k时结论成立。那么当我们新加入一个点时,如果新的竞赛图也是强连通图,那么它向前k个点的连边中,必然存在两种方向不同的边,那么也就是在原来的哈密顿路径上,存在相邻的两个点A和B满足第k+1个点连向他们的边的方向不同。那么也就构成了一条新的哈密顿回路。
接下来考虑如何求n个点的强连通竞赛图数量。
f(n) f ( n ) 表示n个点的竞赛图数量,显然有 f(n)=2n(n1)2 f ( n ) = 2 n ( n − 1 ) 2 。设 g(n) g ( n ) 表示n个点的强连通竞赛图数量。
不难得到递推式

g(n)=f(n)i=1n1g(i)f(ni)Cin g ( n ) = f ( n ) − ∑ i = 1 n − 1 g ( i ) f ( n − i ) C n i

展开一下可以得到
g(n)n!=f(n)n!i=1n1g(i)i!f(ni)(ni)! g ( n ) n ! = f ( n ) n ! − ∑ i = 1 n − 1 g ( i ) i ! f ( n − i ) ( n − i ) !

F(n)=f(n)n!,G(n)=g(n)n! F ( n ) = f ( n ) n ! , G ( n ) = g ( n ) n ! ,那么有 G=FGF G = F − G F ,画一下可以得到 G=FF+1 G = F F + 1
多项式求逆即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

typedef long long LL;

const int N=400005;
const int MOD=998244353;

int n,a[N],b[N],inv[N],rev[N],L,tmp[N],jc[N/4],ny[N/4];

int ksm(int x,int y)
{
    int ans=1;
    while (y)
    {
        if (y&1) ans=(LL)ans*x%MOD;
        x=(LL)x*x%MOD;y>>=1;
    }
    return ans;
}

void NTT(int *a,int f)
{
    for (int i=0;i<L;i++) if (i<rev[i]) std::swap(a[i],a[rev[i]]);
    for (int i=1;i<L;i<<=1)
    {
        int wn=ksm(3,f==1?(MOD-1)/i/2:MOD-1-(MOD-1)/i/2);
        for (int j=0;j<L;j+=(i<<1))
        {
            int w=1;
            for (int k=0;k<i;k++)
            {
                int u=a[j+k],v=(LL)a[j+k+i]*w%MOD;
                a[j+k]=(u+v)%MOD;a[j+k+i]=(u+MOD-v)%MOD;
                w=(LL)w*wn%MOD;
            }
        }
    }
    int ny=ksm(L,MOD-2);
    if (f==-1) for (int i=0;i<L;i++) a[i]=(LL)a[i]*ny%MOD;
}

void get_inv(int *a,int len)
{
    if (len==1) {inv[0]=ksm(a[0],MOD-2);return;}
    get_inv(a,len/2);
    int lg=0;L=len*2;
    for (int w=1;w<L;w<<=1,lg++);
    for (int i=0;i<L;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
    for (int i=0;i<len;i++) tmp[i]=a[i];
    for (int i=len;i<L;i++) tmp[i]=0;
    NTT(tmp,1);NTT(inv,1);
    for (int i=0;i<L;i++) inv[i]=(inv[i]*2%MOD+MOD-(LL)tmp[i]*inv[i]%MOD*inv[i]%MOD)%MOD;
    NTT(inv,-1);
    for (int i=len;i<L;i++) inv[i]=0;
}

int main()
{
    scanf("%d",&n);
    jc[0]=jc[1]=ny[0]=ny[1]=1;
    for (int i=2;i<=n;i++) jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=(LL)(MOD-MOD/i)*ny[MOD%i]%MOD;
    for (int i=2;i<=n;i++) ny[i]=(LL)ny[i-1]*ny[i]%MOD;
    for (int i=1;i<=n;i++) a[i]=b[i]=(LL)ksm(2,(LL)i*(i-1)/2%(MOD-1))*ny[i]%MOD;
    a[0]++;
    for (L=1;L<=n;L<<=1);
    get_inv(a,L); 
    int lg=0;
    for (L=1;L<=n*2;L<<=1,lg++);
    for (int i=0;i<L;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
    for (int i=n+1;i<L;i++) inv[i]=0;
    NTT(b,1);NTT(inv,1);
    for (int i=0;i<L;i++) b[i]=(LL)b[i]*inv[i]%MOD;
    NTT(b,-1);
    for (int i=1;i<=n;i++) b[i]=(LL)b[i]*jc[i]%MOD;
    printf("%d\n",1);
    for (int i=2;i<=n;i++)
        if (!b[i]) puts("-1");
        else printf("%d\n",(LL)jc[i-1]*ksm(2,((LL)i*(i-1)/2%(MOD-1)+MOD-1-i)%(MOD-1))%MOD*ksm(b[i],MOD-2)%MOD);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值