HDU 4609 3-idiots

38 篇文章 0 订阅
14 篇文章 0 订阅
Problem Description
King OMeGa catched three men who had been streaking in the street. Looking as idiots though, the three men insisted that it was a kind of performance art, and begged the king to free them. Out of hatred to the real idiots, the king wanted to check if they were lying. The three men were sent to the king's forest, and each of them was asked to pick a branch one after another. If the three branches they bring back can form a triangle, their math ability would save them. Otherwise, they would be sent into jail.
However, the three men were exactly idiots, and what they would do is only to pick the branches randomly. Certainly, they couldn't pick the same branch - but the one with the same length as another is available. Given the lengths of all branches in the forest, determine the probability that they would be saved.
 

Input
An integer T(T≤100) will exist in the first line of input, indicating the number of test cases.
Each test case begins with the number of branches N(3≤N≤105).
The following line contains N integers a_i (1≤a_i≤105), which denotes the length of each branch, respectively.
 

Output
Output the probability that their branches can form a triangle, in accuracy of 7 decimal places.
 

Sample Input
  
  
2 4 1 3 3 4 4 2 3 3 4
 

Sample Output
  
  
0.5000000 1.0000000
 

Source
 

Recommend

liuyiding

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

FFT+容斥原理+神奇的思路~

……为什么FFT总是被包装得根本看不出来呢?

先把a[i]排序使之单调。

注意到a[i]很小,我们可以开一个数组sum[i]来存a[k]=i的k有多少个,然后sum和自己卷积得到sum[i]数组,表示选取两根木棍使得它们的和为i的方案数,但是这里有同时取了同一根木棍的,所以sum[a[i]+a[i]]--;然后,取ij和取ji是同一种情况,所以sum[i]/=2;再把sum[i]求一下前缀和,方便区间求值。

下面,我们枚举三角形中最大的一边的长度a[i],那么能与它组成三角形的边j、k一定满足a[j]+a[k]>a[i] && j<i && k<i,所以对于一个a[i],操作包括:

ans+=num[maxx]-num[a[i]];

ans-=(ll)(i-1)*(nn-i)(减去jk一大一小的情况);

ans-=nn-1(减去j==i || k==i的情况);

ans-=(ll)(nn-i)*(nn-i-1)/2(减去j>i && k>i的情况)。

然后,ans就是满足条件的组数;选择(不一定满足条件)共有tot=nn*(n-1)*(nn-2)/6种,概率就是ans/tot,注意要强制转化~

另外,由于程序中用到FFT,我用nn来代表题目中的n,而我的n是FFT模板中用到的极限n(或者称为len)~


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long
#define pi acos(-1)
#define N 400040
#define M 100001

int t,n,nn,a[M],maxx,l;
ll num[N],ans,r[N];

struct E{
	double r,i;
	E (double u,double v) {r=u;i=v;}
	E () {}
	E operator + (E u) {return E(u.r+r,u.i+i);}
	E operator - (E u) {return E(r-u.r,i-u.i);}
	E operator * (E u) {return E(r*u.r-i*u.i,r*u.i+i*u.r);}
}f1[N];

E operator / (E u,int v) {return E(u.r/v,u.i/v);}

int read()
{
	int totnum=0;char ch=getchar();
	while(ch<'0' || ch>'9') ch=getchar();
	while(ch>='0' && ch<='9') {totnum=(totnum<<1)+(totnum<<3)+ch-'0';ch=getchar();}
	return totnum;
}

void fft(E *u,int v)
{
	for(int i=0;i<n;i++) if(i<r[i]) swap(u[i],u[r[i]]);
	for(int i=1;i<n;i<<=1)
	{
		E wn(cos(pi/i),v*sin(pi/i));
		for(int j=0;j<n;j+=(i<<1))
		{
			E w(1,0);
			for(int k=0;k<i;k++,w=w*wn)
			{
				E x=u[j+k],y=w*u[i+j+k];
				u[j+k]=x+y;u[i+j+k]=x-y;
			}
		}
	}
	if(v==-1) for(int i=0;i<n;i++) u[i]=u[i]/n;
}

int main()
{
	t=read();
	while(t--)
	{
		nn=read();l=ans=0;
		memset(num,0,sizeof(num));
		for(int i=1;i<=nn;i++) a[i]=read(),num[a[i]]++;
		sort(a+1,a+nn+1);maxx=a[nn]+1;
		for(n=1;n<2*maxx;n<<=1) l++;
		for(int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
		for(int i=0;i<maxx;i++) f1[i]=E(num[i],0);
		for(int i=maxx;i<n;i++) f1[i]=E(0,0);
		fft(f1,1);
		for(int i=0;i<n;i++) f1[i]=f1[i]*f1[i];
		fft(f1,-1);
		for(int i=0;i<n;i++) num[i]=(ll)(f1[i].r+0.5);
		for(int i=1;i<=nn;i++) num[a[i]+a[i]]--;
		for(int i=0;i<=n;i++) num[i]/=2;
		for(int i=1;i<=n;i++) num[i]+=num[i-1];
		for(int i=1;i<=nn;i++)
		{
			ans+=num[n]-num[a[i]];
			ans-=(ll)(i-1)*(nn-i);
			ans-=nn-1;
			ans-=(ll)(nn-i)*(nn-i-1)/2;
		}
		printf("%.7lf\n",(double)ans*6/nn/(nn-1)/(nn-2));
	}
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值