FFT-Super Poker II-UVA - 12298

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表示)。没有其他类型的牌。 p4pppp(pSpHpCpD)

给 定 一 个 正 整 数 n , 从 4 中 花 色 中 各 选 一 张 牌 , 问 有 多 少 种 组 合 数 可 以 使 得 点 数 之 和 等 于 n 。 例 如 , n = 24 时 , 有 一 种 组 合 方 法 是 4 S + 6 H + 4 C + 10 D 。 如 下 图 所 示 : 给定一个正整数n,从4中花色中各选一张牌,问有多少种组合数可以使得点数之和等于n。\\例如,n=24时,有一种组合方法是4S+6H+4C+10D。如下图所示: n4使nn=244S+6H+4C+10D

在这里插入图片描述

但 是 有 c 张 牌 丢 失 了 。 但是有c张牌丢失了。 c

给 定 两 个 正 整 数 a 和 b , 要 求 n = a , n = a + 1 , . . . , n = b 时 的 答 案 。 给定两个正整数a和b,要求n=a,n=a+1,...,n=b时的答案。 abn=a,n=a+1,...,n=b

输入格式

输 入 包 含 不 超 过 25 组 数 据 。 输入包含不超过25组数据。 25

每 组 第 一 行 包 含 3 三 个 正 整 数 a , b , c 每组第一行包含3三个正整数a,b,c 3a,b,c

第 二 行 包 含 c 个 不 同 的 字 符 串 , 即 已 丢 失 的 牌 。 第二行包含c个不同的字符串,即已丢失的牌。 c

这 些 牌 型 如 p S 、 p H 、 p C 或 者 p D , 其 中 p 是 一 个 正 合 数 这些牌型如pS、pH、pC或者pD,其中p是一个正合数 pSpHpCpDp

输 入 结 束 标 志 为 a = b = c = 0 输入结束标志为a=b=c=0 a=b=c=0

输出格式

对 每 组 数 据 , 输 出 b − a + 1 行 , 每 行 一 个 整 数 。 每 组 数 据 后 输 出 一 个 空 行 。 对每组数据,输出b-a+1行,每行一个整数。每组数据后输出一个空行。 ba+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=50000c100001ab1000c10


分析:

用 多 项 式 解 决 此 类 组 合 问 题 。 用多项式解决此类组合问题。

用 四 个 多 项 式 表 示 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+a1x4+a2x6+...+akxp

当 第 i 个 合 数 p i 可 选 择 时 , a i = 1 , 否 则 a i = 0 , 当第i个合数p_i可选择时,a_i=1,否则a_i=0, ipiai=1ai=0

我 们 将 这 4 个 多 项 式 相 乘 , 最 终 的 结 果 就 是 指 数 为 p 的 多 项 式 的 系 数 。 我们将这4个多项式相乘,最终的结果就是指数为p的多项式的系数。 4p

具 体 参 见 刘 汝 佳 白 书 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。 abc0a,b,c0a+b+c=0

④ 、 P I 要 开 l o n g   d o u b l e , 精 度 卡 的 太 严 格 了 ! ④、PI要开long \ double,精度卡的太严格了! PIlong 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值