您的位置:首页 > 其它

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

2017-01-14 17:19 381 查看
题目链接:见这里

题意:给你一个A数组。你要输出所有的T[k]。T[k]是指A数组的所有子集中前k大的数的和的和。

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





代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 4e5 + 10;
const LL G = 3, P = 998244353; //AC
//const LL G = 3, P = 2281701377; //WA
//const LL G = 3, P = 2748779069441; //WA

LL quick(LL x, LL n) {
LL ret = 1;
for(; n; n >>= 1) {
if(n & 1) ret = ret * x % P;
x = x * x % P;
}
return ret;
}

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 op) {
rader(y, len);
for(int h = 2; h <= len; h <<= 1) {
LL wn = quick(G, (P - 1) / h);
if(op == -1) wn = quick(wn, P - 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] % P;
y[k] = (u + t) % P;
y[k + h / 2] = (u - t + P) % P;
w = w * wn % P;
}
}
}
if(op == -1) {
LL inv = quick(len, P - 2);
for(int i = 0; i < len; i++) y[i] = y[i] * inv % P;
}
}

LL inv
, fac
, f
, a
, b
, A
;
LL inv2;
int n;

void pre_init(){
inv2 = quick(2, P - 2);
inv[0] = inv[1] = 1;
fac[0] = fac[1] = 1;
for(LL i = 2; i <= 1e5; i++){
inv[i] = inv[i-1] * quick(i, P-2) % P;
fac[i] = fac[i-1] * i % P;
}
}

void solve1(){ //化成做卷积的两个数组
for(int i = 0; i < n; i++){
a[i] = inv[i] * quick(2, n - i) % P;
}
for(int i = 1; i <= n; i++){
b[i] = fac[i-1] * A[i] % P;
}
reverse(b+1, b+n+1);
for(int i = 0; i < n; i++) b[i] = b[i+1];
b
= 0;
}

void solve2(){ //做ntt
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] % P;
ntt(a, len, -1);
}

void gao(){ //求前缀和,输出答案
LL r = inv2;
for(int i = 1; i <= n; i++){
f[i] = r * inv[i-1] % P * a[n-i] % P;
f[i] = (f[i-1] + f[i]) % P;
printf("%lld ", f[i]);
r = r * inv2 % P; //最后一个数后面还有空格
}
printf("\n");
}

int main()
{
int T;
pre_init();
scanf("%d", &T);
while(T--){
scanf("%d", &n);
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(A, 0, sizeof(A));
for(int i = 1; i <= n; i++) scanf("%lld", &A[i]);
sort(A + 1, A + n + 1, greater<LL>());
solve1();
solve2();
gao();
}
return 0;
}


没搞懂为什么一定要用这个原根才可以AC。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: