HDU 6061 快速数论变换

题目链接


思路:
s u m = − ∑ a i sum = -\sum a_i sum=ai

故对于最终函数的第 k k k项,有:
A n s k = ∑ i = k n c i C i k ( − s u m ) i − k Ans_k = \sum_{i=k}^nc_iC_{i}^{k}(-sum)^{i-k} Ansk=i=knciCik(sum)ik

将组合数展开:
A n s k = ∑ i = k n c i ( − s u m ) i − k i ! k ! ( i − k ) ! Ans_k = \sum_{i=k}^nc_i(-sum)^{i-k}\frac{i!}{k!(i-k)!} Ansk=i=knci(sum)ikk!(ik)!i!

A [ i ] = c i i !   B [ i ] = ( − A ) i i ! A[i] = c_ii! \space B[i] = \frac{(-A)^i}{i!} A[i]=cii! B[i]=i!(A)i
则:
A n s k = ∑ i = k n A [ i ] B [ i − k ] k ! Ans_k =\frac{\sum_{i=k}^nA[i]B[i-k]}{k!} Ansk=k!i=knA[i]B[ik]

考虑将B翻转,使A,B的下标之和为 k k k

A n s k = ∑ i = k n A [ i ] B [ k − i ] k ! Ans_k =\frac{\sum_{i=k}^nA[i]B[k-i]}{k!} Ansk=k!i=knA[i]B[ki]

转化后得到:
A n s n − 1 + k = ∑ i = k n A [ i ] B [ n − 1 + k − i ] k ! Ans_{n-1+k} = \frac{\sum_{i=k}^nA[i]B[n-1+k-i]}{k!} Ansn1+k=k!i=knA[i]B[n1+ki]

然后就可以使用NTT优化了~


代码:

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int P = 998244353;
const int N = 1<<18;
const int G = 3;
int fac[N],inv[N],f[N],Ans[N],fac_sum[N],X[N],Y[N];
int n,m,sum;

int Pow(int n,int m){
    int res = 1;
    while(m){
        if(m&1) res = 1LL*res*n%P;
        n = 1LL*n*n%P;
        m >>= 1;
    }
    return res;
}

void init(int N){
    fac[0] = 1;
    for(int i=1 ;i<=N ;i++) fac[i] = fac[i-1]*1LL*i%P;
    inv[N] = Pow(fac[N],P-2);
    for(int i=N-1 ;i>=0 ;i--) inv[i] = inv[i+1]*1LL*(i+1)%P;
}

int rev[N],w[2][N];
void init_NTT(int n){
    for(int i=0 ;i<n;i++){
        int x=i;int y=0;
        for(int k=1;k<n;k<<=1,x>>=1)(y<<=1)|=(x&1);
        rev[i]=y;
    }
    int v = Pow(G,(P-1)/n);
    int dv = Pow(v,P-2);
    w[0][0]=w[1][0]=1;
    for(int i=1 ;i<n ;i++){
        w[0][i]=w[0][i-1]*1ll*v%P;
        w[1][i]=w[1][i-1]*1ll*dv%P;
    }
}

void NTT(int *A,int n,int ff){
    for(int i=0 ;i<n ;i++) if(i<rev[i])swap(A[i],A[rev[i]]);
    for(int i=1;i<n;i<<=1)
    for(int j=0,t=n/(i<<1);j<n;j+=(i<<1))
    for(int k=0,l=0;k<i;k++,l+=t){
        int x=A[i+j+k]*1ll*w[ff][l]%P;
        int y=A[j+k];
        A[j+k]=(x+y)%P;
        A[i+j+k]=(y+P-x)%P;
    }
    if(ff){
        int v=Pow(n,P-2);
        for(int i=0 ;i<n ;i++) A[i]=A[i]*1ll*v%P;
    }
}

void cal(){
    if(sum == 0){
        for(int i=0 ;i<=n ;i++) Ans[i] = f[i];
        return;
    }
    sum = (P-sum)%P;
    fac_sum[0] = 1;
    for(int i=1 ;i<=n ;i++) fac_sum[i] = fac_sum[i-1]*1LL*sum%P;

    int m = 1;
    while(m <= 2*n+2) m<<=1;
    for(int i=0 ;i<m ;i++) X[i] = Y[i] = 0;
    init_NTT(m);
    for(int i=0 ;i<=n ;i++){
        X[i] = f[i]*1LL*fac[i]%P;
        Y[n-i] = fac_sum[i]*1LL*inv[i]%P;
    }
    NTT(X,m,0);
    NTT(Y,m,0);
    for(int i=0 ;i<m ;i++) X[i] = (X[i]*1LL*Y[i])%P;
    NTT(X,m,1);
    for(int i=0 ;i<=n ;i++) Ans[i] = X[i+n]*1LL*inv[i]%P;
}

void solve(){
    for(int i=0 ;i<=n ;i++) scanf("%d",&f[i]);
    scanf("%d",&m);
    sum = 0;
    for(int i=1 ;i<=m ;i++){int x;scanf("%d",&x);sum = (sum+x)%P;}
    cal();
    for(int i=0 ;i<=n ;i++) printf("%d ",Ans[i]);puts("");
}

int main(){
    init(N-1);
    while(~scanf("%d",&n)) solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值