其他分享
首页 > 其他分享> > 【BZOJ3513-MUTC2013】idiots[生成函数+容斥]

【BZOJ3513-MUTC2013】idiots[生成函数+容斥]

作者:互联网

题意:

给一些长度的木棍,问你构成三角形的方案数。\(n<=10^5\)

思路:

计数问题。三角形构成条件中:两短边和大于第三边即可。
可以用生成函数(fft乘法)统计出所有由两条(不同)边构成的长度和及其方案数。
然后乘上比该和小的总个数。
会发现,对于三条边(三元组),如果构成三角形会被算\(3\)次,否则被算\(2\)次。
因此不构成三元组的数量为\(\binom{n}{3}*3-前面累计的总方案\)。

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
const int N=1e6+5;
const db pi=acos(-1);
struct cop {
	db img,rl;
	friend inline cop operator*(cop u,cop v) {return (cop){u.rl*v.img+u.img*v.rl,u.rl*v.rl-u.img*v.img};}
	friend inline cop operator+(cop u,cop v) {return (cop){u.img+v.img,u.rl+v.rl};}
	friend inline cop operator-(cop u,cop v) {return (cop){u.img-v.img,u.rl-v.rl};}
}f[N];

int rev[N],up,tot[N];
void gt_up(int len) {
	up=1;int L=0;
	while(up<=len){up<<=1;L++;}
	for(int i=1;i<up;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
}

void FFT(cop *a,int op) {
	for(int i=1;i<up;i++)if(rev[i]>i)swap(a[i],a[rev[i]]);
	for(int mid=1;mid<up;mid<<=1) {
		cop w1=(cop){op*sin(pi/mid),cos(pi/mid)};
		for(int len=mid<<1,l=0;l<up;l+=len) {
			cop w=(cop){0,1};
			for(int i=0;i<mid;i++,w=w*w1) {
				int p=l+i,q=p+mid;
				cop x=a[p],y=a[q]*w;
				a[p]=x+y;a[q]=x-y;
			}
		}
	}
}
int a[N],mxa;
int main() {
	int T;scanf("%d",&T);
	while(T--) {
		mxa=0;
		int n;scanf("%d",&n);
		for(int i=0;i<n;i++) {int w;scanf("%d",&a[i]);tot[a[i]]++;mxa=max(mxa,a[i]);}
		int mxa_=mxa<<1;
		gt_up(mxa_);
		for(int i=0;i<up;i++) {f[i]=(cop){0,tot[i]};}
		FFT(f,-1);
		for(int i=0;i<up;i++) {f[i]=f[i]*f[i];}
		FFT(f,1);
		ll res=0,sm=tot[1];
		for(int i=2;i<=mxa_;i++) {
			ll cnt=(ll)(f[i].rl/up+0.5);
			if(!(i&1)) {cnt-=tot[i>>1];}
			cnt>>=1;
			res+=cnt*(sm-2);sm+=tot[i];
		}
		ll Cn_3=1ll*n*(n-1)*(n-2)/6;
		res=Cn_3*3-res;
		printf("%.7lf\n",(db)(Cn_3-res)/Cn_3);
		
		for(int i=1;i<=mxa;i++)tot[i]=0;
	}
	return 0;
}

标签:cop,BZOJ3513,img,int,db,容斥,idiots,res,rl
来源: https://www.cnblogs.com/bestime/p/16442934.html