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)视为一种方案。
所有数据满足:Ai<=40000
来自 https://www.lydsy.com/JudgeOnline/problem.php?id=3771
Solution
数据只给出了a的范围,这是在提示我们与n无关用桶做(滑稽
如果没有奇怪的限制可以直接写成卷积上,但是加上了限制就要考虑容斥了
考虑用a记录一把斧头拿一次的价值,b记录一把斧头拿两次的价值,c记录一把斧头拿三次的价值
答案就是a+(a*a-y*2)/2+(a*a*a-a*b*3-c*2)/6,这里的乘号是卷积
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <complex>
typedef std:: complex <double> com;
typedef double db;
const db pi=3.1415926535897932;
const int N=524289;
int rev[N];
com a[N],b[N],c[N],ans[N];
void FFT(com *a,int len,db f) {
for (int i=0;i<len;i++) if (i<rev[i]) std:: swap(a[i],a[rev[i]]);
for (int i=1;i<len;i*=2) {
com wn(cos(pi/i),f*sin(pi/i));
for (int j=0;j<len;j+=i*2) {
com w(1,0);
for (int k=0;k<i;k++) {
com u=a[j+k],v=a[j+k+i]*w;
a[j+k]=u+v; a[j+k+i]=u-v;
w*=wn;
}
}
}
if (f==-1) for (int i=0;i<len;i++) a[i]/=len;
}
int main(void) {
int n,max=0; scanf("%d",&n);
for (int i=1;i<=n;i++) {
int x; scanf("%d",&x);
a[x]=1; b[x*2]=1; c[x*3]=1;
max=std:: max(max,x*3);
}
int len,lg; for (len=1,lg=0;len<=max*2;len*=2,lg++);
for (int i=0;i<len;i++) rev[i]=(rev[i/2]/2)|((i&1)<<(lg-1));
FFT(a,len,1); FFT(b,len,1); FFT(c,len,1);
for (int i=0;i<len;i++) ans[i]=a[i]+(a[i]*a[i]-b[i])/2.0+(a[i]*a[i]*a[i]-3.0*a[i]*b[i]+c[i]*2.0)/6.0;
FFT(ans,len,-1);
for (int i=0;i<len;i++) if ((int)(0.1+ans[i].real())>0) printf("%d %d\n", i,(int)(0.1+ans[i].real()));
return 0;
}