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 }