例题6.21 超级扑克 II UVa12298

1.题目描述:点击打开链接

2.解题思路:本题利用母函数+FFT解决。根据题意,我们可以构造4个母函数,每个母函数都代表一种花色含有哪些牌,比如,黑桃如果有4,6,8,14这4张牌,那么对应的母函数就是x^4+x^6+x^8+x^14。同理可以得到其他的三个花色的母函数,接下来就是把这4个多项式相乘,那么相乘后x^r项的系数就是这4种当中各选一种之和为r的所有方案数。其中多项式相乘可以用FFT加速,时间复杂度为O(NlogN)。不过这里有一个小问题,我们如何设置每个多项式的长度呢?由于我们只需要a~b之间的结果,因此长度设置为b+1即可。

3.代码:

#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<complex>
#include<functional>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define me(s)  memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int, int> P;


const double PI=acos(-1.0);

typedef complex<double> CD;

inline void FFT(vector<CD> &a, bool inverse)
{
  int n = a.size();
  // 原地快速bit reversal
  for(int i = 0, j = 0; i < n; i++)
    {
        if(j > i) swap(a[i], a[j]);
        int k = n;
        while(j & (k >>= 1)) j &= ~k;
        j |= k;
    }

  double pi = inverse ? -PI : PI;
  for(int step = 1; step < n; step <<= 1)
  {
    double alpha = pi / step;
    for(int k = 0; k < step; k++)
    {
      CD omegak = exp(CD(0, alpha*k));
      for(int Ek = k; Ek < n; Ek += step << 1)
      {                     // Ek是某次DFT合并中E[k]在原始序列中的下标
        int Ok = Ek + step; // Ok是该DFT合并中O[k]在原始序列中的下标
        CD t = omegak * a[Ok]; // 蝴蝶操作:x1 * omega^k
        a[Ok] = a[Ek] - t;  // 蝴蝶操作:y1 = x0 - t
        a[Ek] += t;         // 蝴蝶操作:y0 = x0 + t
      }
    }
  }
  if(inverse)
    for(int i = 0; i < n; i++) a[i] /= n;
}

// 用FFT实现的快速多项式乘法
inline vector<double> operator * (const vector<double>& v1, const vector<double>& v2)
{
  int s1 = v1.size(), s2 = v2.size(), S = 2;
  while(S < s1 + s2) S <<= 1;
  vector<CD> a(S,0), b(S,0); // 把FFT的输入长度补成2的幂,不小于v1和v2的长度之和
  for(int i = 0; i < s1; i++) a[i] = v1[i];
  FFT(a, false);
  for(int i = 0; i < s2; i++) b[i] = v2[i];
  FFT(b, false);
  for(int i = 0; i < S; i++) a[i] *= b[i];
  FFT(a, true);
  vector<double> res(s1 + s2 - 1);
  for(int i = 0; i < s1 + s2 - 1; i++) res[i] = a[i].real(); // 虚部均为0
  return res;
}


const int maxn = 50000 + 10;

int composite[maxn];
void sieve(int n)
{
  int m = (int)sqrt(n+0.5);
  memset(composite, 0, sizeof(composite));
  for(int i = 2; i <= m; i++) if(!composite[i])
    for(int j = i*i; j <= n; j+=i) composite[j] = 1;
}

const char* suites = "SHCD";
int idx(char suit)
{
  return strchr(suites, suit) - suites;
}

int lost[4][maxn];
int main()
{
  sieve(50000);
  int a, b, c;
  while(scanf("%d%d%d", &a, &b, &c) == 3 && a)
  {
    memset(lost, 0, sizeof(lost));
    for(int i = 0; i < c; i++)
    {
      int d; char s;
      scanf("%d%c", &d, &s);
      lost[idx(s)][d] = 1;
    }
    vector<double> ans(1,1), poly;
    for(int s = 0; s < 4; s++)
    {
      poly.clear();
      poly.resize(b+1, 0);
      for(int i = 4; i <= b; i++)
        if(composite[i] && !lost[s][i]) poly[i] = 1.0;
      ans = ans * poly;
      ans.resize(b+1);
    }
    for(int i = a; i <= b; i++)
      printf("%.0lf\n", fabs(ans[i]));
    printf("\n");
  }
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值