【BZOJ3513】[MUTC2013]idiots FFT

【BZOJ3515】[MUTC2013]idiots

Description

给定n个长度分别为a_i的木棒,问随机选择3个木棒能够拼成三角形的概率。

Input

第一行T(T<=100),表示数据组数。
接下来若干行描述T组数据,每组数据第一行是n,接下来一行有n个数表示a_i。
3≤N≤10^5,1≤a_i≤10^5

Output

T行,每行一个整数,四舍五入保留7位小数。

Sample Input

2
4
1 3 3 4
4
2 3 3 4

Sample Output

0.5000000
1.0000000

HINT

T<=20

N<=100000

题解:我们依旧采用补集法,如果我们求出任意s[i]表示选两个木棍使得长度和为i的方案数,那么可以枚举最长边,统计∑s[a[i]]即可。

那么如何求出s数组呢?FFT。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
struct cp
{
	double x,y;
	cp () {}
	cp (double a,double b){x=a,y=b;}
	cp operator + (cp a) {return cp(x+a.x,y+a.y);}
	cp operator - (cp a) {return cp(x-a.x,y-a.y);}
	cp operator * (cp a) {return cp(x*a.x-y*a.y,x*a.y+y*a.x);}
}l1[1<<20],l2[1<<20];
int n,m,len;
int v[1<<20];
ll ans,sum;
ll s[1<<20];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
void FFT(cp *a,int f)
{
	int i,j,k,h;
	cp t;
	for(i=k=0;i<len;i++)
	{
		if(i>k)	swap(a[i],a[k]);
		for(j=(len>>1);(k^=j)<j;j>>=1);
	}
	for(h=2;h<=len;h<<=1)
	{
		cp wn(cos(2*pi*f/h),sin(2*pi*f/h));
		for(j=0;j<len;j+=h)
		{
			cp w(1,0);
			for(k=j;k<j+h/2;k++)	t=w*a[k+h/2],a[k+h/2]=a[k]-t,a[k]=a[k]+t,w=w*wn;
		}
	}
}
void calc(cp *a)
{
	FFT(a,1);
	for(int i=0;i<len;i++)	a[i]=a[i]*a[i];
	FFT(a,-1);
	for(int i=0;i<len;i++)	s[i]+=ll(a[i].x/len+0.1);
}
void work()
{
	n=rd(),m=0;
	int i;
	for(i=1;i<=n;i++)	v[i]=rd(),m=max(m,v[i]<<1);
	for(len=1;len<=m+1;len<<=1);
	for(i=0;i<len;i++)	l1[i]=cp(0,0);
	memset(s,0,sizeof(s));
	for(i=1;i<=n;i++)	l1[v[i]].x+=1,s[v[i]<<1]--;
	calc(l1);
	for(i=1;i<=m;i++)	s[i]+=s[i-1];
	ans=(ll)n*(n-1)*(n-2)/6,sum=0;
	for(i=1;i<=n;i++)	sum+=s[v[i]];
	printf("%.7lf\n",(1-0.5*sum/ans));
}
int main()
{
	int T=rd();
	while(T--)	work();
	return 0;
}//2 4 1 3 3 4 4 2 3 3 4
posted @ 2017-08-15 20:35  CQzhangyu  阅读(485)  评论(0编辑  收藏  举报