【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
4
1 3 3 4
4
2 3 3 4
Sample Output
0.5000000
1.0000000
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