HDU 5279 分治NTT 图的计数

思路:

显然每个子图内都是森林

去掉所有子图1和n都连通且每条大边都存在的情况

直接DP上

NTT优化一波

注意前两项的值..

//By SiriusRen
#include <bits/stdc++.h>
using namespace std;
const int mod=998244353,N=262144;
int cases,n,R[N],fac[N],inv[N],A[N],B[N],h[N],f[N],g[N],jy;
int power(int x,int y){
    int r=1;
    while(y){
        if(y&1)r=1ll*x*r%mod;
        x=1ll*x*x%mod,y>>=1;
    }return r;
}
void NTT(int *a,int f,int m){
    int L=0,n;
    for(n=1;n<m;n<<=1)L++;
    for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    for(int i=0;i<n;i++)if(i<R[i])swap(a[i],a[R[i]]);
    for(int l=1;l<n;l<<=1){
        int wn=power(3,((mod-1)/(l<<1)*f+(mod-1))%(mod-1));
        for(int j=0;j<n;j+=(l<<1)){
            int w=1;
            for(int k=0;k<l;k++,w=1ll*w*wn%mod){
                int x=a[j+k],y=1ll*w*a[j+k+l]%mod;
                a[j+k]=(x+y)%mod,a[j+k+l]=(x-y+mod)%mod;
            }
        }
    }
    if(f==-1){
        int ni=power(n,mod-2);
        for(int i=0;i<n;i++)a[i]=1ll*a[i]*ni%mod;
    }
}
void cdq(int l,int r){
    if(l==r){
        if(l==1)f[l]=1;
        else f[l]=(1ll*f[l]*fac[l-1]+1ll*h[l]*fac[l-1])%mod;
        return;
    }
    int mid=(l+r)>>1;
    cdq(l,mid);
    int len1=mid-l+1,len2=r-l+1,len=1;
    while(len<len1+len2)len<<=1;
    for(int i=0;i<len1;i++)A[i]=1ll*f[l+i]*inv[l+i]%mod;
    for(int i=len1;i<len;i++)A[i]=0;
    for(int i=0;i<len2;i++)B[i]=h[i];
    for(int i=len2;i<len;i++)B[i]=0;
    NTT(A,1,len),NTT(B,1,len);
    for(int i=0;i<len;i++)A[i]=1ll*A[i]*B[i]%mod;
    NTT(A,-1,len);
    for(int i=mid+1;i<=r;i++)f[i]=(f[i]+A[i-l])%mod;
    cdq(mid+1,r);
}
void init(){
    fac[0]=h[1]=1;
    for(int i=1;i<=100000;i++)fac[i]=1ll*fac[i-1]*i%mod;
    inv[100000]=power(fac[100000],mod-2);
    for(int i=99999;~i;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod;
    for(int i=2;i<=100000;i++)h[i]=1ll*power(i,i-2)*inv[i-1]%mod;
    cdq(1,100000),f[0]=1;
    for(int i=0;i<262144;i++)A[i]=B[i]=0;
    for(int i=0;i<=100000;i++)A[i]=1ll*f[i]*inv[i]%mod;
    B[0]=B[1]=0;
    for(int i=2;i<=100000;i++)B[i]=1ll*h[i]*(i-1)%mod;
    NTT(A,1,262144),NTT(B,1,262144);
    for(int i=0;i<262144;i++)g[i]=1ll*A[i]*B[i]%mod; 
    NTT(g,-1,262144),g[1]=1;
    for(int i=2;i<=100000;i++)g[i]=1ll*g[i]*fac[i-2]%mod;
}
int main(){
    scanf("%d",&cases),init();
    while(cases--){
        scanf("%d",&n);int a1=1,a2=1;
        for(int i=1;i<=n;i++)scanf("%d",&jy),a1=1ll*a1*f[jy]%mod,a2=1ll*a2*g[jy]%mod;
        printf("%lld\n",(1ll*a1*power(2,n)%mod-a2+mod)%mod);
    }
}

 

转载于:https://www.cnblogs.com/SiriusRen/p/7114891.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值