2018.11.17 hdu5829Rikka with Subset(ntt)

传送门
n t t ntt ntt基础题。
考虑计算每一个数在排名为 k k k时被统计了多少次来更新答案。
这样的话,设 a n s k ans_k ansk表示所有数的值乘上排名为 k k k的子集数的总和。
a n s k = ∑ i = k n a i ( i − 1 k − 1 ) 2 n − i ans_k=\sum_{i=k}^na_i\binom{i-1}{k-1}2^{n-i} ansk=i=knai(k1i1)2ni
=> a n s k = 1 ( k − 1 ) ! ∑ i = k n a i ( i − 1 ) ! ( i − k ) ! 2 n − i ans_k=\frac1{(k-1)!}\sum_{i=k}^na_i\frac{(i-1)!}{(i-k)!}2^{n-i} ansk=(k1)!1i=knai(ik)!(i1)!2ni
=> a n s k = 1 ( k − 1 ) ! 2 k ∑ i = 0 n − k a i + k ( i + k − 1 ) ! 2 n − i i ! ans_k=\frac1{(k-1)!2^k}\sum_{i=0}^{n-k}a_{i+k}(i+k-1)!\frac{2^{n-i}}{i!} ansk=(k1)!2k1i=0nkai+k(i+k1)!i!2ni
然后令 x i = 2 n − i i ! , y i = a i ( i − 1 ) ! x_i=\frac{2^{n-i}}{i!},y_i=a_i(i-1)! xi=i!2ni,yi=ai(i1)!
那么将 y y y数组翻转再平移一下就可以卷积了。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
    int ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
typedef long long ll;
const int N=4e5+5,mod=998244353;
int tim,lim,n,a[N],b[N],fac[N],ifac[N],pos[N],pow2[N],num[N];
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=(ll)a*a%mod)if(p&1)ret=(ll)ret*a%mod;return ret;}
inline void prepare(){
    ifac[0]=ifac[1]=fac[0]=fac[1]=1,pow2[0]=1;
    for(ri i=1;i<=1e5;++i)pow2[i]=(ll)pow2[i-1]*2%mod;
    for(ri i=2;i<=1e5;++i)ifac[i]=(ll)ifac[mod%i]*(mod-mod/i)%mod,fac[i]=(ll)fac[i-1]*i%mod;
    for(ri i=2;i<=1e5;++i)ifac[i]=(ll)ifac[i-1]*ifac[i]%mod;
}
inline void init(){
    lim=1,tim=0;
    while(lim<=n*2)lim<<=1,++tim;
    for(ri i=0;i<lim;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(tim-1));
}
inline void ntt(int *a,int type){
    for(ri i=0;i<lim;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
    int typ=type==1?3:(mod+1)/3,mult=(mod-1)>>1;
    for(ri mid=1,wn;mid<lim;mid<<=1,mult>>=1){
        wn=ksm(typ,mult);
        for(ri j=0,len=mid<<1;j<lim;j+=len){
            for(ri k=0,w=1;k<mid;++k,w=(ll)w*wn%mod){
                int a0=a[j+k],a1=(ll)a[j+k+mid]*w%mod;
                a[j+k]=(a0+a1)%mod,a[j+k+mid]=(a0-a1+mod)%mod;
            }
        }
    }
    if(type==-1){
        int inv=ksm(lim,mod-2);
        for(ri i=0;i<lim;++i)a[i]=(ll)a[i]*inv%mod;
    }
}
int main(){
    prepare();
    for(int tt=read();tt;--tt,puts("")){
        n=read(),init();
        for(ri i=1;i<=n;++i)num[i]=read();
        sort(num+1,num+n+1),reverse(num+1,num+n+1);
        for(ri i=0;i<n;++i)a[i]=(ll)ifac[i]*pow2[n-i]%mod;
        for(ri i=1;i<=n;++i)b[i]=(ll)fac[i-1]*num[i]%mod;
        reverse(b+1,b+n+1);
        for(ri i=0;i<n;++i)b[i]=b[i+1];
        b[n]=0;
        ntt(a,1),ntt(b,1);
        for(ri i=0;i<lim;++i)a[i]=(ll)a[i]*b[i]%mod;
        ntt(a,-1);
        for(ri inv=(mod+1)/2,ans=0,last=0,i=1;i<=n;++i){
            ans=((ll)inv*ifac[i-1]%mod*a[n-i]%mod+last)%mod;
            printf("%d ",last=ans);
            inv=(ll)inv*(mod+1)/2%mod;
        }
        memset(num,0,sizeof(num)),memset(a,0,sizeof(a)),memset(b,0,sizeof(b));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值