BZOJ 3771 母函数裸题
题目描述
我们讲一个悲伤的故事。
从前有一个贫穷的樵夫在河边砍柴。
这时候河里出现了一个水神,夺过了他的斧头,说:
“这把斧头,是不是你的?”
樵夫一看:“是啊是啊!”
水神把斧头扔在一边,又拿起一个东西问:
“这把斧头,是不是你的?”
樵夫看不清楚,但又怕真的是自己的斧头,只好又答:“是啊是啊!”
水神又把手上的东西扔在一边,拿起第三个东西问:
“这把斧头,是不是你的?”
樵夫还是看不清楚,但是他觉得再这样下去他就没法砍柴了。
于是他又一次答:“是啊是啊!真的是!”
水神看着他,哈哈大笑道:
“你看看你现在的样子,真是丑陋!”
之后就消失了。
樵夫觉得很坑爹,他今天不仅没有砍到柴,还丢了一把斧头给那个水神。
于是他准备回家换一把斧头。
回家之后他才发现真正坑爹的事情才刚开始。
水神拿着的的确是他的斧头。
但是不一定是他拿出去的那把,还有可能是水神不知道怎么偷偷从他家里拿走的。
换句话说,水神可能拿走了他的一把,两把或者三把斧头。
樵夫觉得今天真是倒霉透了,但不管怎么样日子还得过。
他想统计他的损失。
樵夫的每一把斧头都有一个价值,不同斧头的价值不同。总损失就是丢掉的斧头价值和。
他想对于每个可能的总损失,计算有几种可能的方案。
注意:如果水神拿走了两把斧头a和b,(a,b)和(b,a)视为一种方案。拿走三把斧头时,(a,b,c),(b,c,a),(c,a,b),(c,b,a),(b,a,c),(a,c,b)视为一种方案。
输入格式
第一行是整数N,表示有N把斧头。
接下来n行升序输入N个数字Ai,表示每把斧头的价值。
输出格式
若干行,按升序对于所有可能的总损失输出一行x y,x为损失值,y为方案数。
样例输入
4 4 5 6 7
样例输出
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.
提示
所有数据满足:Ai<=40000
题意: 从n个物品中选取1个或 2个 或 3个 的价值和是多少 对于一个价值 输出方案数 (方案无顺序要求)
简单讲一下母函数 例如有3个物品 他们的价值是1,2,3
构造一个母函数 f(x) = x^1 + x^2 + x^3 (表示取一件)
下面我来解释一下这个f(x) 指数为物品的价值 每一项前面的系数表示方案数
x^1 表示取一件物品取到价值为 1 的方案数为 1
x^2 表示取一件物品取到价值为 2 的方案数为 1 以此类推
g(x)= x^2 + x^4 + x^6 (表示同一件物品取两次)
z(x)= x^3 + x^6 + x^9 (表示同一件物品取三次)
那么取两次而且方案数不重复的结果是 ( f ( x ) * f ( x ) - g ( x ) ) / 2 (f ( x ) * f ( x ) 会多算了一次取两个相同的方案 所以要减去 )
取三次的方案就是 ( f ( x ) * f ( x ) * f(x)-3 * f ( x ) * g(x) +2 * z(x) )/6
(无顺序要求所以要除以一个3的全排列 f (x) * g (x) / 2 多算了的取了两个相同的方案数 因为下面有一个6的分母 所以乘以了一个系数 3
但是这里面还包括了选取了3个都相同的方案 所以要加上 2*z(x))
这里面的多项式的计算就直接通过FFT优化就好了 (FFT板子当作黑盒直接使用就行了)
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #include <cmath> 5 #include <algorithm> 6 #include <set> 7 #include <iostream> 8 #include <map> 9 #include <stack> 10 #include <string> 11 #include <vector> 12 #define pi acos(-1.0) 13 #define eps 1e-9 14 #define fi first 15 #define se second 16 #define rtl rt<<1 17 #define rtr rt<<1|1 18 #define bug printf("******\n") 19 #define mem(a,b) memset(a,b,sizeof(a)) 20 #define name2str(x) #x 21 #define fuck(x) cout<<#x" = "<<x<<endl 22 #define f(a) a*a 23 #define sf(n) scanf("%d", &n) 24 #define sff(a,b) scanf("%d %d", &a, &b) 25 #define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c) 26 #define sffff(a,b,c,d) scanf("%d %d %d %d", &a, &b, &c, &d) 27 #define pf printf 28 #define FRE(i,a,b) for(i = a; i <= b; i++) 29 #define FREE(i,a,b) for(i = a; i >= b; i--) 30 #define FRL(i,a,b) for(i = a; i < b; i++)+ 31 #define FRLL(i,a,b) for(i = a; i > b; i--) 32 #define FIN freopen("data.txt","r",stdin) 33 #define gcd(a,b) __gcd(a,b) 34 #define lowbit(x) x&-x 35 #define rep(i,a,b) for(int i=a;i<b;++i) 36 #define per(i,a,b) for(int i=a-1;i>=b;--i) 37 using namespace std; 38 typedef long long LL; 39 typedef unsigned long long ULL; 40 const int maxn = 3e5 + 7; 41 const int maxm = maxn * 4; 42 int n, m, a[maxn], b[maxn]; 43 int len, res[maxm], mx; //开大4倍 44 struct cpx { 45 double r, i; 46 cpx ( double r = 0, double i = 0 ) : r ( r ), i ( i ) {}; 47 cpx operator+ ( const cpx &b ) { 48 return cpx ( r + b.r, i + b.i ); 49 } 50 cpx operator- ( const cpx &b ) { 51 return cpx ( r - b.r, i - b.i ); 52 } 53 cpx operator* ( const cpx &b ) { 54 return cpx ( r * b.r - i * b.i, i * b.r + r * b.i ); 55 } 56 } va[maxm], vb[maxm]; 57 void rader ( cpx F[], int len ) { //len = 2^M,reverse F[i] with F[j] j为i二进制反转 58 int j = len >> 1; 59 for ( int i = 1; i < len - 1; ++i ) { 60 if ( i < j ) swap ( F[i], F[j] ); // reverse 61 int k = len >> 1; 62 while ( j >= k ) j -= k, k >>= 1; 63 if ( j < k ) j += k; 64 } 65 } 66 void FFT ( cpx F[], int len, int t ) { 67 rader ( F, len ); 68 for ( int h = 2; h <= len; h <<= 1 ) { 69 cpx wn ( cos ( -t * 2 * pi / h ), sin ( -t * 2 * pi / h ) ); 70 for ( int j = 0; j < len; j += h ) { 71 cpx E ( 1, 0 ); //旋转因子 72 for ( int k = j; k < j + h / 2; ++k ) { 73 cpx u = F[k]; 74 cpx v = E * F[k + h / 2]; 75 F[k] = u + v; 76 F[k + h / 2] = u - v; 77 E = E * wn; 78 } 79 } 80 } 81 if ( t == -1 ) //IDFT 82 for ( int i = 0; i < len; ++i ) F[i].r /= len; 83 } 84 void Conv ( cpx a[], cpx b[], int len ) { //求卷积 85 FFT ( a, len, 1 ); 86 FFT ( b, len, 1 ); 87 for ( int i = 0; i < len; ++i ) a[i] = a[i] * b[i]; 88 FFT ( a, len, -1 ); 89 } 90 void gao () { 91 len = 1; 92 mx = n + m; 93 while ( len <= mx ) len <<= 1; //mx为卷积后最大下标 94 for ( int i = 0; i < len; i++ ) va[i].r = va[i].i = vb[i].r = vb[i].i = 0; 95 for ( int i = 0; i < n; i++ ) va[i].r = a[i]; //根据题目要求改写 96 for ( int i = 0; i < m; i++ ) vb[i].r = b[i]; //根据题目要求改写 97 Conv ( va, vb, len ); 98 for ( int i = 0; i < len; ++i ) res[i] += va[i].r + 0.5; 99 } 100 int B[maxn], C[maxn], ans[maxm], cnt1[maxm], cnt2[maxm]; 101 int main() { 102 FIN; 103 sf ( n ); 104 int maxxA = -1; 105 for ( int i = 0, x; i < n ; i++ ) { 106 sf ( x ); 107 a[x]++, b[x]++, ans[x]++; 108 maxxA = max ( maxxA, x ); 109 B[2 * x]++, C[3 * x]++; 110 } 111 n = m = ++maxxA; 112 gao(); 113 for ( int i = 0 ; i <= 2 * maxxA ; i++ ) ans[i] += ( res[i] - B[i] ) / 2, b[i] = res[i], res[i] = 0; 114 m = 2 * maxxA; 115 gao(); 116 for ( int i = 0 ; i <= 3 * maxxA ; i++ ) cnt1[i] = res[i], res[i] = 0; 117 for ( int i = 0 ; i <= 2 * maxxA ; i++ ) b[i] = B[i]; 118 gao(); 119 for ( int i = 0 ; i <= 3 * maxxA ; i++ ) cnt2[i] = res[i]; 120 for ( int i = 0 ; i <= 3 * maxxA ; i++ ) ans[i] += ( cnt1[i] - 3 * cnt2[i] + 2 * C[i] ) / 6; 121 for ( int i = 0 ; i <= 3 * maxxA ; i++ ) if ( ans[i] ) printf ( "%d %d\n", i, ans[i] ); 122 return 0; 123 }