FFT可以解决这样一种问题:
有k种元素,均有无穷多个,规定第i种元素选取的个数ci必须属于一个特定的集合Si,当需要选取r个元素时,有多少种选取方式?
Exp:有苹果,香蕉和桃子3种水果,如果苹果只能选不超过3个,选香蕉的个数必须是5的倍数,而桃子的个数必须是素数,问选r个水果有几种方法。
Solution:解决方法是把每个集合写成一个多项式,使得每一项x^i的系数取决于i是否在集合中存在:如果存在,则系数等于1;否则,系数等于0。这样苹果对应的多项式是1+x+x2+x3…香蕉x5+x10+x15…桃子x2
+x3+x5+x7…然后把多项式乘起来,就行了,结果中xr的系数就是选r个元素的方法数。
FFT 就可以把多项相乘n方复杂度降为nlogn
例题
Super Poker II UVA - 12298
题目大意:
有一副超级扑克牌,超级扑克牌由四个花色组成,每个花色都有无数张牌,扑克牌的面值p满足条件:p的约数的个数大于2,即扑克牌的面值为:4,6,8,9,10,12,…(不包括1和素数的自然数) 现在这幅扑克牌丢失了c张,问你从剩余的扑克牌的4个花色中各选出一张牌来,四个花色的面值之和组成一个新的值,问分别有多少种选法能组成值a, a+1, a+2,…,b。 输出b-a+1行,每行一个数代表选法的个数。
思路:
跟选水果的那个思路一样,这里是四个多项式相乘,注意开数组大小。
#include <bits/stdc++.h>
using namespace std;
// -------------------- FFT --------------------/
typedef long double db;
const db PI = acos(-1.0);
struct Complex {
db x, y;
Complex(db x=0.0, db y=0.0) : x(x), y(y) {}
Complex operator - (const Complex &b) const {
return Complex(x-b.x, y-b.y);
}
Complex operator + (const Complex &b) const {
return Complex(x+b.x, y+b.y);
}
Complex operator * (const Complex &b) const {
return Complex(x*b.x-y*b.y, x*b.y+y*b.x);
}
};
void change(Complex 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 fft(Complex y[], int len, int on) {
change(y, len);
for(int h = 2; h <= len; h<<=1) {
Complex wn(cos(-on*2*PI/h), sin(-on*2*PI/h));
for(int j = 0; j < len; j+=h) {
Complex w(1, 0);
for(int k = j; k < j + h / 2; k++) {
Complex u = y[k];
Complex t = w*y[k+h/2];
y[k] = u + t;
y[k+h/2] = u - t;
w = w*wn;
}
}
}
if(on == -1) {
for(int i = 0; i < len; i++) {
y[i].x /= len;
}
}
}
// -------------------------FFT-------------------------/
int idx(char c) {
if(c == 'S') return 0;
else if(c == 'H') return 1;
else if(c == 'C') return 2;
else return 3;
}
typedef long long ll;
int a, b, c;
const int maxb = 50000 + 100;
Complex x[4][maxb*4*2];
bool notprime[maxb];
void init() {
memset(notprime, false, sizeof(notprime));
notprime[0] = notprime[1] = true;
for(int i = 2; i < maxb; i++) {
if(!notprime[i]) {
if(i > maxb/i) break;
for(int j = i * i; j < maxb; j+=i) {
notprime[j] = true;
}
}
}
}
int main()
{
// freopen("/Users/maoxiangsun/MyRepertory/acm/i.txt", "r", stdin);
init();
int cas = 0;
while(~scanf("%d%d%d", &a, &b, &c) && a) {
int len = 1, len0 = b + 1;
while(len<4*len0) len<<=1;
for(int k = 0; k < 4; k++) {
x[k][0] = x[k][1] = Complex(0.0, 0.0);
for(int i = 2; i < len0; i++) {
if(notprime[i]) x[k][i] = Complex(1.0, 0.0);
else x[k][i] = Complex(0.0, 0.0);
}
for(int i = len0; i < len; i++) {
x[k][i] = Complex(0.0, 0.0);
}
}
for(int i = 0; i < c; i++) {
int p;char cc;
scanf("%d%c", &p, &cc);
x[idx(cc)][p].x = 0.0;
}
for(int i = 0; i < 4; i++) {
fft(x[i], len, 1);
}
for(int i = 0; i < len; i++) {
x[0][i] = x[0][i] * x[1][i] * x[2][i] * x[3][i];
}
fft(x[0], len, -1);
for(int i = a; i <= b; i++) {
printf("%lld\n", (ll)(x[0][i].x + 0.5));
}
printf("\n");
}
return 0;
}
/*
12 20 2
4S 6H
0 0 0
*/