HDU 5829 Rikka with Subset(NTT)

9 篇文章 0 订阅

Description
给出一个长度为n的序列A,定义一个非空集合S的价值为这个集合第min{|S|,k}大的数,定义一个序列的价值为这个序列的所有非空子集价值之和,对[1,n]中任一k,求A序列的价值
Input
第一行一整数T表示用例组数,每组用例首先输入一整数n表示序列长度,之后n个整数Ai表示该序列(1<=T<=10,1<=n<=10^5,0<=Ai<=10^9)
Output
对于每组用例,输出k个整数分别表示对任一k,A序列的价值,结果模 998244353
Sample Input
2
3
1 1 1
5
1 2 3 4 5
Sample Output
7 11 12
129 201 231 239 240
Solution
定义ans[k]为A序列所有非空子集第k大元素之和(如果某子集元素个数小于k则不考虑),先给A序列从小到大排序,考虑每个Ai作为第k大的数时对ans[k]的贡献,那么有
这里写图片描述
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef long double ld;
#define maxn 111111
#define mod 998244353 
#define g 3
ll wn[22],iwn[22];
ll mul(ll a,ll b,ll p)
{
    //return (a*b-(ll)(a/(ld)p*b+1e-3)*p+p)%p;
    return a*b%p;
}
ll mod_pow(ll a,ll b,ll p)
{
    ll ans=1;
    while(b)
    {
        if(b&1)ans=mul(ans,a,p);
        a=mul(a,a,p);
        b>>=1;
    }
    return ans;
}
int pos[4*maxn];
void fft_init(int len)
{
    int j=0;
    while((1<<j)<len)j++;
    j--;
    for(int i=0;i<len;i++)
        pos[i]=pos[i>>1]>>1|((i&1)<<j);
}
void ntt(ll *x,int len,int sta) 
{
    for(int i=0;i<len;i++)
        if(i<pos[i])swap(x[i],x[pos[i]]);
    for(int m=2,now=1;m<=len;now++,m<<=1)
    {
        ll Wn=wn[now];
        //ll Wn=mod_pow(g,(mod-1)/m,mod);
        if(sta==-1)Wn=iwn[now];
        ll W=1;
        for(int k=0;k<m/2;k++,W=mul(W,Wn,mod))
        {
            for(int i=k;i<len;i+=m)
            {
                ll temp=mul(W,x[i+m/2],mod);
                x[i+m/2]=x[i]-temp<0?x[i]-temp+mod:x[i]-temp;
                x[i]=x[i]+temp>=mod?x[i]+temp-mod:x[i]+temp;
            }
        }
    }
    if(sta==-1)
    {
        ll temp=mod_pow(len,mod-2,mod);
        for(int i=0;i<len;i++)x[i]=mul(x[i],temp,mod);
    }
}
void NTT(ll *a,ll *b,int len1,int len2)
{
    int len=1;
    while(len<len1+len2)len<<=1;
    fft_init(len);
    for(int i=len1;i<len;i++)a[i]=0;
    for(int i=len2;i<len;i++)b[i]=0;
    ntt(a,len,1),ntt(b,len,1);
    for(int i=0;i<len;i++)a[i]=mul(a[i],b[i],mod);
    ntt(a,len,-1);
}
ll f[maxn],b[maxn],inv[maxn];
void init()
{
    f[0]=b[0]=inv[0]=1;
    for(int i=1;i<maxn;i++)
    {
        b[i]=2ll*b[i-1]%mod;
        f[i]=1ll*i*f[i-1]%mod;
        inv[i]=mod_pow(f[i],mod-2,mod);
    }
    int j=18;
    wn[j+1]=mod_pow(g,(mod-1)/(1<<(j+1)),mod);
    for(int i=j;i>=0;i--)wn[i]=mul(wn[i+1],wn[i+1],mod);
    iwn[j+1]=mod_pow(wn[j+1],mod-2,mod);
    for(int i=j;i>=0;i--)iwn[i]=mul(iwn[i+1],iwn[i+1],mod);
}
bool cmp(ll a,ll b)
{
    return a>b;
}
int T,n;
ll a[maxn],A[4*maxn],B[4*maxn],ans[maxn];
int main()
{
    init();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)scanf("%I64d",&a[i]);
        sort(a,a+n,cmp);
        for(int i=0;i<n;i++)A[n-i-1]=a[i]*f[i]%mod*b[n-i-1]%mod;
        for(int i=0;i<n;i++)B[i]=inv[i];
        NTT(A,B,n,n);
        for(int i=1;i<=n;i++)ans[i]=A[n-i]*inv[i-1]%mod;
        for(int i=2;i<=n;i++)ans[i]=(ans[i]+ans[i-1])%mod;
        for(int i=1;i<=n;i++)printf("%I64d ",ans[i]);
        printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值