bzoj3771 Triple

题意:你有若干把斧头,河神拿了你的1/2/3把斧头,问可能拿了你的斧头的总价值,每个总价值有多少种方案。

斧头价值不大于40000

解:

很容易想到是FFT,构造函数之后A + A² + A3即可。

然后发现漏洞百出,这道题的难点是在容斥上......

首先只选一个的不用管了,肯定对。

然后考虑选两个的。你直接卷积,有可能把同一把斧头选两次,还会把一个方案的两个不同排列当两次计算。

所以我们先减去把统一斧头选两次的情况,然后把结果 / 2就是选两个的答案。

选三个呢?我们还要构造一个函数B,代表强制把某斧头选两次,剩下的随便选。

然后考虑选两个同样的斧头的情况:在A3中会被计算三次,因为排列有三种。在AB中会被计算一次。

选三个同样的情况:在A3中有一次,在AB中有一次。

三个不同的情况:在A3中有6次。在AB中没有。

综上,可以用A3 - 3AB + 2*(每个斧头选三次) 即可。

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cstring>
  4 #include <cmath>
  5 
  6 const int N = 250010;
  7 const double pi = 3.1415926535897932384626;
  8 
  9 struct cp {
 10     double x, y;
 11     cp(double X = 0, double Y = 0) {
 12         x = X;
 13         y = Y;
 14     }
 15     inline cp operator +(const cp &w) const {
 16         return cp(x + w.x, y + w.y);
 17     }
 18     inline cp operator -(const cp &w) const {
 19         return cp(x - w.x, y - w.y);
 20     }
 21     inline cp operator *(const cp &w) const {
 22         return cp(x * w.x - y * w.y, x * w.y + y * w.x);
 23     }
 24 }a[N], b[N], c[N];
 25 
 26 int r[N], val[N], ans[N], ans2[N];
 27 
 28 inline void FFT(int n, cp *a, int f) {
 29     for(int i = 0; i < n; i++) {
 30         if(i < r[i]) {
 31             std::swap(a[i], a[r[i]]);
 32         }
 33     }
 34     for(int len = 1; len < n; len <<= 1) {
 35         cp Wn(cos(pi / len), f * sin(pi / len));
 36         for(int i = 0; i < n; i += (len << 1)) {
 37             cp w(1, 0);
 38             for(int j = 0; j < len; j++) {
 39                 cp t = a[i + len + j] * w;
 40                 a[i + len + j] = a[i + j] - t;
 41                 a[i + j] = a[i + j] + t;
 42                 w = w * Wn;
 43             }
 44         }
 45     }
 46     if(f == -1) {
 47         for(int i = 0; i <= n; i++) {
 48             a[i].x /= n;
 49         }
 50     }
 51     return;
 52 }
 53 
 54 int main() {
 55     int n, mx = 0;
 56     scanf("%d", &n);
 57     for(int i = 1; i <= n; i++) {
 58         scanf("%d", &val[i]);
 59         a[val[i]].x += 1;
 60         b[val[i] << 1].x += 1;
 61         mx = std::max(mx, val[i]);
 62     }
 63     int len = 2, lm = 1;
 64     while(len <= mx * 3) {
 65         len <<= 1;
 66         lm++;
 67     }
 68     for(int i = 1; i <= len; i++) {
 69         r[i] = (r[i >> 1] >> 1) | ((i & 1) << (lm - 1));
 70     }
 71 
 72     FFT(len, b, 1);
 73     FFT(len, a, 1);
 74     for(int i = 0; i <= len; i++) {
 75         c[i] = a[i] * b[i];
 76         b[i] = a[i] * a[i];
 77         a[i] = a[i] * b[i];
 78     }
 79     FFT(len, a, -1); // 取三个
 80     FFT(len, b, -1); // 取两个
 81     FFT(len, c, -1); // 取三个,有两个一样
 82 
 83     for(int i = 0; i <= len; i++) {
 84         ans[i] += (int)(a[i].x + 0.5);
 85         ans[i] -= (int)(c[i].x + 0.5) * 3;
 86     }
 87     for(int i = 1; i <= n; i++) {
 88         ans[val[i] * 3] += 2;
 89         ans2[val[i] * 2]--;
 90     }
 91     for(int i = 0; i <= len; i++) {
 92         ans[i] /= 6;
 93         ans2[i] += (int)(b[i].x + 0.5);
 94         ans2[i] /= 2;
 95     }
 96 
 97     for(int i = 1; i <= n; i++) {
 98         ans[val[i]]++;
 99     }
100 
101     for(int i = 0; i <= len; i++) {
102         if(ans[i] + ans2[i]) {
103             printf("%d %d \n", i, ans[i] + ans2[i]);
104         }
105     }
106     return 0;
107 }
AC代码

 

posted @ 2019-01-14 08:15  huyufeifei  阅读(108)  评论(0编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

ReadEra 阅读书籍

『Flyable Heart 応援中!』