Bzoj3771 Triple

Time Limit: 20 Sec  Memory Limit: 64 MB
Submit: 563  Solved: 317

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

Source

 

数学问题 生成函数 FFT 容斥

题面挺带感的233

设x[]为只拿一把斧头的方案构建的指数生成函数,y[]为拿了相同两把斧头的方案构建的指数生成函数,z[]为拿了相同三把斧头的方案构建的指数生成函数。

那么可以容斥计算:

  拿一把的方案数:$O(n)$直接统计

  拿两把的方案数:$ans=(x*x-y)/2$

  拿三把的方案数: $ans= (x*x*x-3*x*y+2*z)/6 $

 

 1 /*by SilverN*/
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cstdio>
 6 #include<cmath>
 7 #include<vector>
 8 #define LL long long
 9 using namespace std;
10 const double pi=acos(-1.0);
11 const int mxn=160010;
12 int read(){
13     int x=0,f=1;char ch=getchar();
14     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
15     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
16     return x*f;
17 }
18 struct  com{
19     double x,y;
20     com operator + (com b){return (com){x+b.x,y+b.y};}
21     com operator - (com b){return (com){x-b.x,y-b.y};}
22     com operator * (com b){return (com){x*b.x-y*b.y,x*b.y+y*b.x};}
23     com operator / (double v){return (com){x/v,y/v};}
24 }a[mxn],b[mxn],c[mxn];
25 int N,l,rev[mxn];
26 void FFT(com *a,int flag){
27     for(int i=0;i<N;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
28     for(int i=1;i<N;i<<=1){
29         com wn=(com){cos(pi/i),flag*sin(pi/i)};
30         for(int j=0;j<N;j+=i<<1){
31             com w=(com){1,0};
32             for(int k=0;k<i;k++,w=w*wn){
33                 com x=a[k+j],y=w*a[k+j+i];
34                 a[k+j]=x+y;
35                 a[k+j+i]=x-y;
36             }
37         }
38     }
39     if(flag==-1)for(int i=0;i<N;i++) a[i].x/=N;
40     return;
41 }
42 int n,w[mxn];
43 LL ans[mxn],d[mxn];
44 int main(){
45     int i,j,mx=0;
46     n=read();
47     for(i=1;i<=n;i++)w[i]=read(),mx=max(mx,w[i]);
48     int m=mx*3;
49     for(N=1;N<m;N<<=1)l++;
50     for(i=0;i<=N;i++)
51         rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
52     for(i=1;i<=n;i++){++ans[w[i]];}//只拿一把 
53     //
54     for(i=1;i<=n;i++)a[w[i]].x+=1;
55     FFT(a,1);
56     for(i=0;i<=N;i++)
57         a[i]=a[i]*a[i];
58     FFT(a,-1);
59     for(i=0;i<=N;i++)d[i]+=(int)(a[i].x+0.3);
60     for(i=1;i<=n;i++)d[w[i]+w[i]]--;
61     for(i=0;i<=N;i++)ans[i]+=d[i]/2;
62     //拿两把 
63     memset(a,0,sizeof a);
64     for(i=1;i<=n;i++)a[w[i]].x++;
65     FFT(a,1);
66     for(i=0;i<=N;i++) a[i]=a[i]*a[i]*a[i];
67     FFT(a,-1);
68     for(i=0;i<=N;i++)d[i]=(int)(a[i].x+0.3);
69     memset(a,0,sizeof a);
70     for(i=1;i<=n;i++){
71         a[w[i]].x+=1;
72         b[w[i]+w[i]].x+=1;
73     }
74     FFT(a,1);FFT(b,1);
75     for(i=0;i<=N;i++)c[i]=a[i]*b[i];
76     FFT(c,-1);
77     for(i=0;i<=N;i++)d[i]-=((int)(c[i].x+0.3))*3;
78     for(i=1;i<=n;i++){d[w[i]*3]+=2;}
79     for(i=0;i<=N;i++)ans[i]+=d[i]/6;
80     //拿三把 
81     for(i=0;i<=N;i++)
82         if(ans[i])
83             printf("%d %lld\n",i,ans[i]);
84     return 0;
85 }

 

本文为博主原创文章,转载请注明出处。
posted @ 2017-04-20 08:43  SilverNebula  阅读(100)  评论(0编辑  收藏  举报
AmazingCounters.com