HDU 5829 16多校08 Rikka with Subset (NTT)

Rikka with Subset

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 871    Accepted Submission(s): 276

Problem Description
As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:
Yuta has n numbers A[1]~A[n] and a number K. For any none empty subset S of the numbers, the value of S is equal to the sum of the largest min(|S|,k) numbers in S. The value of the array A is equal to the sum of the value of all none empty subset of the numbers.
Now Yuta shows the n numbers, And he wants to know the value of the array for each K in [1,n].
It is too difficult for Rikka. Can you help her?

Input
The first line contains a number t(1<=t<=10), the number of the testcases.
For each testcase, the first line contains a number n(1<=n<=100000), the number of numbers Yuta has. The second line contains n number A[1]~A[n](0<=A[i]<=10^9).
Output
For each testcase, print a line contains exactly n numbers, the ith number is the value of the array when K=i. The answer may be very large, so you only need to print the answer module 998244353.
Sample Input
  
  
2 3 1 1 1 5 1 2 3 4 5
Sample Output
  
  
7 11 12 129 201 231 239 240
Author
学军中学
Source

Recommend
wange2014   |   We have carefully selected several similar problems for you:  5932 5931 5930 5929 5928
题意:给你一个A数组。你要输出所有的T[k]。T[k]是指A数组的所有子集中前k大的数的和的和。
题解:FFT的原理,NTT的板子(P需要是费马素数...有空我再写一篇NTT(快速数论变换)的文章吧....

假设一个数g对于P来说是原根,那么的结果两两不同,且有 1<g<P,0<i<P,那么g可以称为是P的一个原根,归根到底就是当且仅当指数为P-1的时候成立.(这里P是素数).

所以这时就可以利用卷积来解决了。
先从大到小排序,排序之后发现,第i个数对于k的贡献只当它作为集合中最大,第二大…第k大的时候才有。那么我们可以想一个比较暴力的方法,枚举每个i作为集合第k大的贡献,在求个前缀和,就是答案。
设f[k]为所有数作为第k大的时候的和,那么有





以上就是卷积的套路了。设 ,。先把b数组reverse一下,,再整体左移一位就是。此时就是卷积形式了。注意最后是

AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int inf = 0x3f3f3f3f;
const int maxn = 4e5 + 10;
const int maxv = 1e3 + 10;
const double eps = 1e-9;
const ll mod = 998244353;
const ll g = 3; // MOD的原根,当且仅当 g^(MOD-1) = 1 % MOD
ll Pow(ll a, ll n)
{
    ll ans = 1;
    while(n)
	{
        if(n & 1) ans = ans * a % mod;
		n >>= 1;
        a = a * a % mod;
    }
    return ans;
}
void rader(ll y[], int len)
{
    for(int i = 1, j = len / 2; i < len - 1; i++){
        if(i < j) swap(y[i], y[j]);
        int k = len / 2;
        while(j >= k) {
            j -= k;
            k /= 2;
        }
        if(j < k) j += k;
    }
}
void ntt(ll y[], int len, int on) //NTT
{
    rader(y, len);
    for(int h = 2; h <= len; h <<= 1){
        ll wn = Pow(g, (mod-1)/h);
        if(on == -1) wn = Pow(wn, mod-2);
        for(int j = 0; j < len; j += h) {
            ll w = 1;
            for(int k = j; k < j + h / 2; k++) {
                ll u = y[k];
                ll t = w * y[k + h / 2] % mod;
                y[k] = (u + t) % mod;
                y[k+h/2] = (u - t + mod) % mod;
                w = w * wn % mod;
            }
        }
    }
    if(on == -1) {
        ll t = Pow(len, mod - 2);
        for(int i = 0; i < len; i++)
            y[i] = y[i] * t % mod;
    }
}
ll inv[maxn];
ll fac[maxn];
ll f[maxn];
ll inv2;
ll a[maxn], b[maxn];
ll A[maxn];
int n;
void init()
{
    inv2 = Pow(2, mod - 2);
    inv[1] = inv[0] = 1;
    fac[0] = fac[1] = 1;
    for(ll i = 2; i <= 1e5; i++)
	{
        inv[i] = inv[i-1] * Pow(i, mod - 2) % mod;
        fac[i] = fac[i-1] * i % mod;
    }
}
void solve()
{
	for(int i = 0; i < n; i++)
	{
        a[i] = inv[i] * Pow(2, n-i) % mod;
    }
    for(int i = 1; i <= n; i++)
	{
           b[i] = fac[i-1] * A[i] % mod;
    }
    reverse(b+1, b+1+n);
    for(int i = 0; i < n; i++)
	{
        b[i] = b[i+1];
    }
    b[n] = 0;
}
void Conv()
{
	int len = 1;
    while(len <= 2*n) len <<= 1;
    ntt(a, len, 1);
    ntt(b, len, 1);
    for(int i = 0; i < len; i++)
	{
        a[i] = a[i] * b[i] % mod;
    }
    ntt(a, len, -1);
}
void gao()
{
	ll r = inv2;
    for(int i = 1; i <= n; i++)
	{
        f[i] = r * inv[i-1] % mod * a[n-i] % mod;
        f[i] = (f[i-1] + f[i]) % mod;
        printf("%lld ", f[i]);
        r = r * inv2 % mod;
    }
    puts(""); //最后一个数后也有空格..... 
}

int main()
{

    int t;
    scanf("%d", &t);
    init();
    while(t--)
	{ 
        scanf("%d", &n);
        memset(A, 0, sizeof(A));
        memset(a, 0, sizeof(a));
        memset(b, 0, sizeof(b));
        for(int i = 1; i <= n; i++)
		{
            scanf("%lld", &A[i]);
        }
        sort(A+1, A+n+1, greater<ll>()); 
		solve();     
        Conv();        
        gao();
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值