FFT-Super Poker II-UVA - 12298
题意:
有 一 副 扑 克 , 包 含 无 数 张 牌 。 对 于 每 个 正 合 数 p , 恰 好 又 4 张 牌 : 黑 桃 p , 红 桃 p , 梅 花 p 和 方 块 p ( 分 别 用 p S 、 p H 、 p C 和 p D 表 示 ) 。 没 有 其 他 类 型 的 牌 。 有一副扑克,包含无数张牌。对于每个正合数p,恰好又4张牌:黑桃p,红桃p,\\梅花p和方块p(分别用pS、pH、pC和pD表示)。没有其他类型的牌。 有一副扑克,包含无数张牌。对于每个正合数p,恰好又4张牌:黑桃p,红桃p,梅花p和方块p(分别用pS、pH、pC和pD表示)。没有其他类型的牌。
给 定 一 个 正 整 数 n , 从 4 中 花 色 中 各 选 一 张 牌 , 问 有 多 少 种 组 合 数 可 以 使 得 点 数 之 和 等 于 n 。 例 如 , n = 24 时 , 有 一 种 组 合 方 法 是 4 S + 6 H + 4 C + 10 D 。 如 下 图 所 示 : 给定一个正整数n,从4中花色中各选一张牌,问有多少种组合数可以使得点数之和等于n。\\例如,n=24时,有一种组合方法是4S+6H+4C+10D。如下图所示: 给定一个正整数n,从4中花色中各选一张牌,问有多少种组合数可以使得点数之和等于n。例如,n=24时,有一种组合方法是4S+6H+4C+10D。如下图所示:
但 是 有 c 张 牌 丢 失 了 。 但是有c张牌丢失了。 但是有c张牌丢失了。
给 定 两 个 正 整 数 a 和 b , 要 求 n = a , n = a + 1 , . . . , n = b 时 的 答 案 。 给定两个正整数a和b,要求n=a,n=a+1,...,n=b时的答案。 给定两个正整数a和b,要求n=a,n=a+1,...,n=b时的答案。
输入格式
输 入 包 含 不 超 过 25 组 数 据 。 输入包含不超过25组数据。 输入包含不超过25组数据。
每 组 第 一 行 包 含 3 三 个 正 整 数 a , b , c 每组第一行包含3三个正整数a,b,c 每组第一行包含3三个正整数a,b,c
第 二 行 包 含 c 个 不 同 的 字 符 串 , 即 已 丢 失 的 牌 。 第二行包含c个不同的字符串,即已丢失的牌。 第二行包含c个不同的字符串,即已丢失的牌。
这 些 牌 型 如 p S 、 p H 、 p C 或 者 p D , 其 中 p 是 一 个 正 合 数 这些牌型如pS、pH、pC或者pD,其中p是一个正合数 这些牌型如pS、pH、pC或者pD,其中p是一个正合数
输 入 结 束 标 志 为 a = b = c = 0 输入结束标志为a=b=c=0 输入结束标志为a=b=c=0
输出格式
对 每 组 数 据 , 输 出 b − a + 1 行 , 每 行 一 个 整 数 。 每 组 数 据 后 输 出 一 个 空 行 。 对每组数据,输出b-a+1行,每行一个整数。每组数据后输出一个空行。 对每组数据,输出b−a+1行,每行一个整数。每组数据后输出一个空行。
数据范围:
最 多 一 组 数 据 满 足 a = 1 , b = 50000 且 c ≤ 10000 , 其 他 数 据 1 ≤ a ≤ b ≤ 100 , 0 ≤ c ≤ 10 最多一组数据满足a=1,b=50000且c\le 10000,其他数据1\le a\le b\le 100,0\le c\le 10 最多一组数据满足a=1,b=50000且c≤10000,其他数据1≤a≤b≤100,0≤c≤10
分析:
用 多 项 式 解 决 此 类 组 合 问 题 。 用多项式解决此类组合问题。 用多项式解决此类组合问题。
用 四 个 多 项 式 表 示 4 种 牌 的 选 法 集 合 。 用四个多项式表示4种牌的选法集合。 用四个多项式表示4种牌的选法集合。
每 个 多 项 式 对 应 为 : 1 + a 1 ⋅ x 4 + a 2 ⋅ x 6 + . . . + a k ⋅ x p , 每个多项式对应为:1+a_1·x^4+a_2·x^6+...+a_k·x^p, 每个多项式对应为:1+a1⋅x4+a2⋅x6+...+ak⋅xp,
当 第 i 个 合 数 p i 可 选 择 时 , a i = 1 , 否 则 a i = 0 , 当第i个合数p_i可选择时,a_i=1,否则a_i=0, 当第i个合数pi可选择时,ai=1,否则ai=0,
我 们 将 这 4 个 多 项 式 相 乘 , 最 终 的 结 果 就 是 指 数 为 p 的 多 项 式 的 系 数 。 我们将这4个多项式相乘,最终的结果就是指数为p的多项式的系数。 我们将这4个多项式相乘,最终的结果就是指数为p的多项式的系数。
具 体 参 见 刘 汝 佳 白 书 P 430 。 具体参见刘汝佳白书P430。 具体参见刘汝佳白书P430。
注意:
① 、 最 后 输 出 时 , 要 先 将 结 果 强 转 为 l o n g l o n g , 再 输 出 , 否 则 精 度 会 出 现 问 题 。 ①、最后输出时,要先将结果强转为long\ long,再输出,否则精度会出现问题。 ①、最后输出时,要先将结果强转为long long,再输出,否则精度会出现问题。
② 、 多 项 式 的 长 度 根 据 多 项 式 最 终 乘 积 的 最 高 次 数 来 确 定 。 ②、多项式的长度根据多项式最终乘积的最高次数来确定。 ②、多项式的长度根据多项式最终乘积的最高次数来确定。
③ 、 a 、 b 、 c 均 为 0 , 在 a , b , c ≥ 0 的 情 况 下 , 判 断 条 件 是 a + b + c = 0 。 ③、a、b、c均为0,在a,b,c\ge0的情况下,判断条件是a+b+c=0。 ③、a、b、c均为0,在a,b,c≥0的情况下,判断条件是a+b+c=0。
④ 、 P I 要 开 l o n g d o u b l e , 精 度 卡 的 太 严 格 了 ! ④、PI要开long \ double,精度卡的太严格了! ④、PI要开long double,精度卡的太严格了!
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cmath>
#define ll long long
using namespace std;
const long double PI = acos(-1.0);
struct Complex
{
long double x, y;
Complex(long double _x = 0.0, long double _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)
{
int i, j, k;
for(int i=1, j=len/2; i<len-1; i++)
{
if(i<j) swap(y[i], y[j]);
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;
}
const int N = 5e4+10;
Complex x[4][N*16]; //最多四个多项式相乘
int a,b,c;
char str[10];
int primes[N], cnt;
bool st[N];
void get_prime(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]*i<=n;j++)
{
st[i*primes[j]]=true;
if(i%primes[j]==0) break;
}
}
}
int main()
{
get_prime(N-1);
while(~scanf("%d%d%d",&a,&b,&c), a + b + c)
{
int len = 1;
while(len <= b) len <<= 1; //有4个多项式乘积,次数最多为4b,
len <<= 3; //即n=4b,则2n=8b
for(int j=0;j<4;j++)
for(int i=0;i<=b;i++)
if(st[i]) x[j][i] = Complex(1,0);
else x[j][i] = Complex(0,0); //多测 要清空
for(int j=0;j<4;j++)
for(int i=b+1;i<len;i++)
x[j][i] = Complex(0,0); //虽然没有必要,但是要注意步骤
for(int i=0;i<c;i++)
{
scanf("%s",str);
int k = strlen(str), id;
if(str[k-1]=='S') id = 0;
else if(str[k-1]=='H') id = 1;
else if(str[k-1]=='C') id = 2;
else id = 3;
str[k-1]='\0';
int m = atoi(str);
x[id][m] = Complex(0,0);
}
for(int i=0;i<4;i++) fft(x[i], len, 1);
for(int i=1;i<4;i++)
for(int j=0;j<len;j++)
x[0][j] = x[0][j]*x[i][j];
fft(x[0], len, -1);
for(int i=a;i<=b;i++) printf("%lld\n",(ll)(x[0][i].x+0.5));
puts("");
}
return 0;
}