codeforces 632E FFT快速幂优化

简略题意: n 种物品,每种物品有对应的权值,每种物品的数量没有限制。现在问取k个物品能够组成的权值有哪些。

假若只能取两个物品,就是一个很经典的生成函数问题,相当于做一次多项式乘法,可以用FFT去优化这个过程。现在我们做 k 次多项式乘法,就可以用快速幂去加速这个过程。
复杂度O(maxvklog(maxvlogk)log(k))

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const LL kMaxn = 4e6;
typedef long double ld;
const double kPi = acos(-1.0);
int len, n;

struct Complex {
    double r, i;
    Complex(double r = 0, double i = 0):r(r), i(i) {};
    Complex operator + (const Complex & rhs) {
        return Complex(r + rhs.r, i + rhs.i);
    }
    Complex operator - (const Complex & rhs) {
        return Complex(r - rhs.r, i - rhs.i);
    }
    Complex operator * (const Complex &rhs) {
        return Complex(r * rhs.r - i * rhs.i, i * rhs.r + r * rhs.i);
    }
} va[kMaxn], vb[kMaxn];

void Rader(Complex F[], int len) {
    int j = len >> 1;
    for(int i = 1; i < len - 1; i++) {
        if(i < j) swap(F[i], F[j]);
        int k = len >> 1;
        while(j >= k) {
            j -= k;
            k >>= 1;
        }
        if(j < k) j += k;
    }
}

void FFT(Complex F[], int len, int t) { //时域转频域
    Rader(F, len);
    for(int h = 2; h <= len; h <<= 1) {
        Complex wn(cos(-t*2*kPi/h), sin(-t*2*kPi/h));
        for(int j = 0; j < len; j += h) {
            Complex E(1, 0);
            for(int k = j; k < j + h / 2; k++) {
                Complex u = F[k];
                Complex v = E*F[k+h/2];
                F[k] = u + v;
                F[k+h/2] = u - v;
                E = E * wn;
            }
        }
    }
    if(t == -1) {
        for(int i = 0; i < len; i++) {
            F[i].r /= len;
        }
    }
}

void Init(int *a, int *b, int &v1, int v2) {
    int n1 = v1, n2 = v2;
    len = 1;
    while(len < 2*n1 || len < 2*n2) len <<= 1;
    int i;
    for(i = 0; i < n1; i++)
        va[i].r = a[i], va[i].i = 0;
    while(i < len) va[i].r = va[i].i = 0, i++;
    for(i = 0; i < n2; i++)
        vb[i].r = b[i], vb[i].i = 0;
    while(i < len) vb[i].r = vb[i].i = 0, i++;
    v1 += v2;
}

void Solve(int *a, int *b, int &v1, int v2) {
    Init(a, b, v1, v2);
    FFT(va, len, 1);
    FFT(vb, len, 1);
    for(int i = 0; i < len; i++) va[i] = va[i] * vb[i];
    FFT(va, len, -1);
    for(int i = 0; i < len; i++)
        a[i] = (int)(va[i].r + 0.5)?1:0;
}

int t, k;
int res[kMaxn];
int a[kMaxn], b[kMaxn];
int v1, v2;

int main() {
    scanf("%d%d", &t, &k);
    for(int i = 1; i <= t; i++) {
        int v;
        scanf("%d", &v);
        a[v]++;
        v1 = max(v1, v);
    }
    v1++;
    v2 = 1;
    res[0] = 1;
    while(k) {
        if(k & 1)
            Solve(res, a, v2, v1);
        k>>=1;
        Solve(a, a, v1, v1);
    }
    for(int i = 0 ; i<= v2; i++)
        if(res[i])
            printf("%d\n", i);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值