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("******
")
 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
", i, ans[i] );
122     return 0;
123 }
原文地址:https://www.cnblogs.com/qldabiaoge/p/10431947.html