BZOJ3771-生成函数,容斥

BZOJ3771

题目描述

给出 n n n个物品,价值为别为 X i Xi Xi且各不相同,现在可以取 1 1 1个、 2 2 2个或 3 3 3个,问每种价值和有几种情况?
顺序不同算一种

题解

定义 A ( x ) , B ( x ) , C ( x ) A(x),B(x),C(x) A(x),B(x),C(x)分别表示每种物品取1个,2个,3个的生成函数。
那么分三种情况计算答案。
1,取1个物品
A n s = A ( x ) Ans=A(x) Ans=A(x)
2,取2个物品
A n s = A ( x ) 2 − B ( x ) 2 Ans=\frac{A(x)^2-B(x)}{2} Ans=2A(x)2B(x)
3,取3个物品
A n s = A ( x ) 3 − 3 A ( x ) B ( x ) + 2 C ( x ) 6 Ans=\frac{A(x)^3-3A(x)B(x)+2C(x)}{6} Ans=6A(x)33A(x)B(x)+2C(x)

代码

#include  <cmath>
#include  <cstdio>
#include  <climits>
#include  <cstring>
#include  <cstdlib>
#include  <iostream>
#include  <algorithm>
using namespace std;
const int M=4e5+9;
const double pi=acos(-1.0);
int n,m,r[M],l,lim=1;
struct complex{
	double x,y;
	complex(double xx=0,double yy=0){x=xx,y=yy;}
	friend inline complex operator+(complex a,complex b){return complex(a.x+b.x,a.y+b.y);}
	friend inline complex operator-(complex a,complex b){return complex(a.x-b.x,a.y-b.y);}
	friend inline complex operator*(complex a,complex b){return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
	friend inline complex operator/(complex a,double b){return complex(a.x/b,a.y/b);}
	friend inline complex operator*(complex a,double b){return complex(a.x*b,a.y*b);}
}a[M],b[M],c[M],ans[M];
void FFT(complex *A,int type){
	for(int i=0;i<lim;i++) if(i<r[i]) swap(A[i],A[r[i]]);
	for(int mid=1;mid<lim;mid<<=1){
		complex W(cos(pi/mid),type*sin(pi/mid));
		for(int R=mid<<1,j=0;j<lim;j+=R){
			complex w(1,0);
			for(int k=0;k<mid;k++,w=w*W){
				complex x=A[j+k],y=w*A[j+k+mid];
				A[j+k]=x+y;
				A[j+mid+k]=x-y;
			}
		}
	}if(type==-1) for(int i=0;i<lim;i++) A[i]=A[i]/lim;
}
signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x;scanf("%d",&x); 
		a[x].x++,b[x*2].x++,c[x*3].x++;
		m=max(m,x*3);
	}
	while(lim<m*2) lim<<=1,l++;
	for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	FFT(a,1),FFT(b,1),FFT(c,1);
	for(int i=0;i<=lim;i++) ans[i]=(a[i]*a[i]*a[i]-3.0*a[i]*b[i]+2.0*c[i])/6.0+(a[i]*a[i]-b[i])/2.0+a[i];
	FFT(ans,-1);
	for(int i=0;i<=m;i++)
		if((int)(ans[i].x+0.5)) printf("%d %d\n",i,(int)(ans[i].x+0.5));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值