HDU 5829 Rikka with Subset

题意: 给一个数组A[n], 要求出所有的T[k],T[i]指A数组的所有子集中前k大的和的和。
虽然只是用FFT的原理,NTT的模板。

将代码中的A和B卷积,然后再乘上,各自对应的系数1/(2……k*(k-1)!);
需要需用:费马小定理,费马素数 FFT等等。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<cstdlib>
#include<ctime>
#include<set>
#include<math.h>
using namespace std;
typedef long long ll;
const int nn = 400100;
const int inf = (1 << 31);
const ll mod = 998244353;
const ll g = 3;//mod的原根,当且仅当 g^(MOD-1) = 1 % MOD
#define pb push_back
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

ll n,m,q;
ll a[nn];
ll inv[nn];//i的阶乘的倒数%mod
ll fac[nn]; //i的阶乘
ll f[nn];//记录答案
ll A[nn];//2^(n-i)/i!;
ll B[nn];//i!*a[i+1];
ll inv2;
ll ppow(ll x, ll n)
{
	ll ans = 1;
	while (n)
	{
		if (n & 1) ans = ans*x%mod;
		x = x*x%mod;
		n >>= 1;
	}
	return ans;
}
void init()
{
	inv2 = ppow(2ll, mod - 2);
	inv[1] = inv[0] = 1;
	fac[0] = fac[1] = 1;
	for (ll i = 2; i <= 1e5; i++)
	{
		inv[i] = inv[i - 1] * ppow(i, mod - 2)%mod;
		fac[i] = fac[i - 1] * i%mod;
	}
}

void rader(ll y[], ll len)
{
	for (ll i = 1, j = len / 2; i < len-1; i++)
	{
		if (i < j) swap(y[i], y[j]);
		ll k = len / 2;
		while (j >= k) {
			j -= k;
			k /= 2;
		}
		if (j < k)j += k;
	}
}
void ntt(ll y[], int len, int on) {
	rader(y, len);
	for (int h = 2; h <= len; h <<= 1) {
		ll wn = ppow(g, (mod - 1) / h); //****//
		if (on  == -1) wn = ppow(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 = ppow(len, mod - 2);
		for (int i = 0; i < len; i++)
			y[i] = y[i] * t % mod;
	}
}

bool cmp(ll x, ll y)
{
	return x > y;
}
int main()
{
	int t;
	scanf("%d", &t);
	init();
	while(t--)
	{
		scanf("%lld", &n);
		memset(A, 0, sizeof(A));
		memset(B, 0, sizeof(B));
		memset(a, 0, sizeof(a));
		for (ll i = 1; i <= n; i++) scanf("%lld", &a[i]);
		sort(a + 1, a + n + 1,cmp);
		for (ll i = 0; i < n; i++) A[i] = inv[i] * ppow(2, n-i) % mod;
		for (ll i = 1; i <= n; i++) B[i] = fac[i - 1] * a[i] % mod;
		reverse(B + 1, B + n + 1);
		for (ll i = 0; i < n; i++) B[i] = B[i + 1];
		B[n] = 0;
		ll len = 1;
		while (len <= n * 2)len <<= 1;
		ntt(A, len, 1);
		ntt(B, len, 1);
		for (ll i = 0; i < len; i++) A[i] = A[i] * B[i] % mod;
		ntt(A, len, -1);
		ll r = inv2;
		for (ll i = 1; i <= n; i++)
		{
			f[i] = r*inv[i - 1] % mod*A[n - i] % mod;
			f[i] = (f[i] + f[i - 1]) % mod;
			printf("%lld ", f[i]);
			r = r*inv2%mod;
		}
		puts("");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值