BZOJ3771: Triple【生成函式】
Description
我們講一個悲傷的故事。
從前有一個貧窮的樵夫在河邊砍柴。
這時候河裡出現了一個水神,奪過了他的斧頭,說:
“這把斧頭,是不是你的?”
樵夫一看:“是啊是啊!”
水神把斧頭扔在一邊,又拿起一個東西問:
“這把斧頭,是不是你的?”
樵夫看不清楚,但又怕真的是自己的斧頭,只好又答:“是啊是啊!”
水神又把手上的東西扔在一邊,拿起第三個東西問:
“這把斧頭,是不是你的?”
樵夫還是看不清楚,但是他覺得再這樣下去他就沒法砍柴了。
於是他又一次答:“是啊是啊!真的是!”
水神看著他,哈哈大笑道:
“你看看你現在的樣子,真是醜陋!”
之後就消失了。
樵夫覺得很坑爹,他今天不僅沒有砍到柴,還丟了一把斧頭給那個水神。
於是他準備回家換一把斧頭。
回家之後他才發現真正坑爹的事情才剛開始。
水神拿著的的確是他的斧頭。
但是不一定是他拿出去的那把,還有可能是水神不知道怎麼偷偷從他家裡拿走的。
換句話說,水神可能拿走了他的一把,兩把或者三把斧頭。
樵夫覺得今天真是倒黴透了,但不管怎麼樣日子還得過。
他想統計他的損失。
樵夫的每一把斧頭都有一個價值,不同斧頭的價值不同。總損失就是丟掉的斧頭價值和。
他想對於每個可能的總損失,計算有幾種可能的方案。
注意:如果水神拿走了兩把斧頭a和b,(a,b)和(b,a)視為一種方案。拿走三把斧頭時,(a,b,c),(b,c,a),(c,a,b),(c,b,a),(b,a,c),(a,c,b)視為一種方案。
Input
第一行是整數N,表示有N把斧頭。
接下來n行升序輸入N個數字Ai,表示每把斧頭的價值。
Output
若干行,按升序對於所有可能的總損失輸出一行x y,x為損失值,y為方案數
Sample Input
4
4
5
6
7
Sample Output
4 1
5 1
6 1
7 1
9 1
10 1
11 2
12 1
13 1
15 1
16 1
17 1
18 1
樣例解釋
11有兩種方案是4+7和5+6,其他損失值都有唯一方案,例如4=4,5=5,10=4+6,18=5+6+7.
HINT
所有資料滿足:Ai<=40000
思路
考慮一個兩個三個分別的生成函式
容斥掉不合法貢獻
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
const double eps = 1e-6;
const double PI = acos(-1);
typedef complex<double> Complex;
Complex f[N], g[N], h[N], p[N], q[N], w[N][2];
void init() {
for (int i = 1; i < (1 << 18); i <<= 1) {
w[i][0] = w[i][1] = Complex(1, 0);
Complex wn(cos(PI / i), sin(PI / i));
for (int j = 1; j < i; j++)
w[i + j][1] = w[i + j - 1][1] * wn;
wn = Complex(cos(PI / i), -sin(PI / i));
for (int j = 1; j < i; j++)
w[i + j][0] = w[i + j - 1][0] * wn;
}
}
void transform(Complex t[N], int len, int typ) {
for (int i = 0, j = 0, k; j < len; j++) {
if (i > j) swap(t[i], t[j]);
for (k = (len >> 1); k & i; k >>= 1) i ^= k;
i ^= k;
}
for (int i = 1; i < len; i <<= 1) {
for (int j = 0; j < len; j += (i << 1)) {
for (int k = 0; k < i; k++) {
Complex x = t[j + k], y = t[j + k + i] * w[i + k][typ];
t[j + k] = x + y;
t[j + k + i] = x - y;
}
}
}
if (typ) return;
for (int i = 0; i < len; i++)
t[i] = Complex(t[i].real() / (double) len, t[i].imag());
}
int n, a[N];
int main() {
#ifdef dream_maker
freopen("input.txt", "r", stdin);
#endif
init();
int maxval = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
maxval = max(maxval, a[i]);
}
maxval = maxval * 3 + 1;
for (int i = 1; i <= n; i++) {
f[a[i]] = Complex(f[a[i]].real() + 1, f[a[i]].imag());
p[a[i] * 2] = Complex(p[a[i] * 2].real() + 1, p[a[i] * 2].imag());
q[a[i] * 3] = Complex(q[a[i] * 3].real() + 1, q[a[i] * 3].imag());
}
int len = 1 << (int) ceil(log2(maxval * 2 - 1));
transform(f, len, 1);
for (int i = 0; i < len; i++)
g[i] = f[i] * f[i];
transform(g, len, 0);
for (int i = 0; i < len; i++)
g[i] -= p[i];
transform(g, len, 1);
for (int i = 0; i < len; i++)
h[i] = f[i] * f[i] * f[i];
transform(h, len, 0);
transform(p, len, 1);
for (int i = 0; i < len; i++)
p[i] = f[i] * p[i];
transform(g, len, 0);
transform(f, len, 0);
transform(p, len, 0);
for (int i = 0; i <= len; i++) {
h[i] -= p[i] + p[i] + p[i];
h[i] += q[i] + q[i];
}
for (int i = 0; i <= maxval; i++)
if ((int) (round(f[i].real()) + round(g[i].real()) / 2 + round(h[i].real()) / 6))
printf("%d %d\n", i, (int) (round(f[i].real()) + round(g[i].real()) / 2 + round(h[i].real()) / 6));
return 0;
}