HDU 5829 Rikka with Subset(NTT)

题意

给定 \(n\) 个数 \(a_1,a_2,\cdots a_n\),对于每个 \(K\in[1,n]\) ,求出 \(n\) 个数的每个子集的前 \(K\) 大数的和,输出每个值,对 \(998244353\) 取模。

\(1\leq n \leq 10^5\)

思路

\(K\)\(k\) 时的答案为 \(ans_k\)


\[ ans_k=\sum_{i=1}^na_i2^{n-i}\sum_{j=0}^{k-1}{i-1\choose j} \]
\(j\) 为在 \(a_i\) 的左边选了多少个数。定义当\(i<j\)\(\displaystyle{i\choose j}=0\) ,即当 \(n<0\)\(\displaystyle{1\over n!}=0\)

有两个\(\sum\) ,导致难以化简,但是我们发现差分后只有一个 \(\sum\)

\(d_k=ans_k-ans_{k-1}\) ,则有
\[ d_k=\sum_{i=1}^na_i2^{n-i}{i-1\choose k-1}\\ d_k=(k-1)!\sum_{i=1}^na_i2^{n-i}(i-1)!\cdot{1\over{(i-k)!}} \]
\(i+k\) 替换 \(k\) ,并化成卷积形式
\[ d_{i+k}=(i+k-1)!a_i2^{n-i}(i-1)!\cdot{1\over{(-k)!}} \]
其中 \(i\in[1,n],i+k\in[1,n],k\in[1-n,n-1]\)

\(\displaystyle A_i=a_i2^{n-i}(i-1)!,B_k={1\over{(-k)!}}\)

\(d_{i+k}=(i+k-1)A_iB_k\)

处理出 \(A,B\) 两多项式,进行卷积求解即可。

代码

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long ll;
using namespace std;
const int P=998244353,g=3;
const int N=1<<17|5;
namespace Maths
{
    ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
    void exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b){x=1,y=0;return;}
        exgcd(b,a%b,y,x),y-=a/b*x;
    }
    ll Pow(ll a,ll p,ll P)
    {
        ll res=1;
        for(;p>0;p>>=1,(a*=a)%=P)if(p&1)(res*=a)%=P;
        return res;
    }
    ll inv(ll a,ll P){ll x,y;exgcd(a,P,x,y);return (x%P+P)%P;}
};
using namespace Maths;
namespace _NTT
{
    const int g=3,P=998244353;
    int A[N<<1],B[N<<1];
    int w[N<<1],r[N<<1];
    void NTT(int *a,int op,int n)
    {
        FOR(i,0,n-1)if(i<r[i])swap(a[i],a[r[i]]);
        for(int i=2;i<=n;i<<=1)
            for(int j=0;j<n;j+=i)
                for(int k=0;k<i/2;k++)
                {
                    int u=a[j+k],t=(ll)w[op==1?n/i*k:n-n/i*k]*a[j+k+i/2]%P;
                    a[j+k]=(u+t)%P;
                    a[j+k+i/2]=(u-t)%P;
                }
    }
    void multiply(int *a,int *b,int *c,int n1,int n2)
    {
        int n=1;
        while(n<n1+n2-1)n<<=1;
        FOR(i,0,n1-1)A[i]=a[i];
        FOR(i,0,n2-1)B[i]=b[i];
        FOR(i,n1,n-1)A[i]=0;
        FOR(i,n2,n-1)B[i]=0;
        FOR(i,0,n-1)r[i]=(r[i>>1]>>1)|((i&1)*(n>>1));
        w[0]=1,w[1]=Pow(g,(P-1)/n,P);
        FOR(i,2,n)w[i]=(ll)w[i-1]*w[1]%P;
        
        NTT(A,1,n),NTT(B,1,n);
        FOR(i,0,n-1)A[i]=(ll)A[i]*B[i]%P;
        NTT(A,-1,n);
        int I=inv(n,P);
        FOR(i,0,n1+n2-2)c[i]=((ll)A[i]*I%P+P)%P;
    }
};
int A[N],B[N],C[N<<2];
int fac[N],c[N],S;
int n,m;

int main()
{
    fac[0]=1;FOR(i,1,N-1)fac[i]=(ll)fac[i-1]*i%P;
    while(~scanf("%d",&n))
    {
        FOR(i,0,n)scanf("%d",&c[i]);
        scanf("%d",&m);
        S=0;
        while(m--)
        {
            int x;
            scanf("%d",&x);
            S-=x;
            if(S<0)S+=P;
        }
        FOR(i,0,n)A[i]=(ll)c[i]*fac[i]%P;
        FOR(i,-n,0)B[i+n]=Pow(S,-i,P)*inv(fac[-i],P)%P;
        _NTT::multiply(A,B,C,n+1,n+1);
        FOR(i,0,n)printf("%lld ",(C[i+n]*inv(fac[i],P)%P+P)%P);
        puts("");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Paulliant/p/10272268.html