Problem
给定一些长度为xi的线段,在其中随机选取三条线段,问你能组成三角形的概率。
Solution
我们考虑用桶把所有线段装起来,然后对这个桶卷积一下,当然还需要减去选取的两条线段相同的情况,不考虑顺序就再除以2就可以得到两两组合的线段分布。卷积用FFT优化。
那么我们就考虑如何统计答案。先将之前得到的数组前缀和一下。对于线段xi,我们不妨假定xi是构成线段中最长的一条,那么另两条线段和自然要大于它,即贡献为
sum[maxlen]−sum[xi]
s
u
m
[
m
a
x
l
e
n
]
−
s
u
m
[
x
i
]
。当然,你还需要使得选取的是合法的,即xi必须是最长的且不能被选中两次,那么就需要减去选了更长和选了xi的情况。
那么我们就不妨对x数组进行排序。则选了更长的贡献为
(n−i)∗(i−1)+(n−i)∗(n−i−1)/2
(
n
−
i
)
∗
(
i
−
1
)
+
(
n
−
i
)
∗
(
n
−
i
−
1
)
/
2
,分别是选了一条更长的和一条更短的,选了两条更长的。选了自己的贡献为
n−1
n
−
1
,就是选了自己之后再任意选一条。
总情况就是
C3n
C
n
3
。
Code
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=100010;
const double pi=acos(-1.0);
struct cpx{
double r,i;
cpx(){}
cpx(double _r,double _i){r=_r;i=_i;}
cpx operator + (const cpx &x)const{return cpx(r+x.r,i+x.i);}
cpx operator - (const cpx &x)const{return cpx(r-x.r,i-x.i);}
cpx operator * (const cpx &x)const{return cpx(r*x.r-i*x.i,r*x.i+i*x.r);}
cpx operator *= (const cpx &x){return *this=*this*x;}
}a[maxn<<2],b[maxn<<2];
template <typename Tp> inline void read(Tp &x)
{
x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
int z,n,len,l,x[maxn],t[maxn],r[maxn<<2];
ll ans,tot,sum[maxn<<2];
void input()
{
read(n);ans=0;l=0;
memset(t,0,sizeof(t));
for(rg int i=1;i<=n;i++){read(x[i]);t[x[i]]++;}
sort(x+1,x+n+1);
for(len=1;len<=x[n]+x[n]+2;len<<=1) l++;
for(rg int i=1;i<=len;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
a[0]=cpx(0.0,0.0);
for(rg int i=1;i<=x[n];i++) a[i]=cpx((double)t[i],0.0);
for(rg int i=x[n]+1;i<=len;i++) a[i]=cpx(0.0,0.0);
}
void fft(cpx *a,int f)
{
for(int i=1;i<=len;i++)
if(i<r[i]) swap(a[i],a[r[i]]);
for(int i=1;i<len;i<<=1)
{
cpx wn(cos(pi/i),f*sin(pi/i));
for(int j=0;j<len;j+=(i<<1))
{
cpx w(1.0,0.0);
for(int k=0;k<i;k++,w*=wn)
{
cpx x=a[j+k],y=w*a[j+k+i];
a[j+k]=x+y;a[j+k+i]=x-y;
}
}
}
if(f==-1)
for(int i=0;i<len;i++) a[i].r/=len;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
scanf("%d",&z);
while(z--)
{
input();
fft(a,1);
for(rg int i=0;i<len;i++) b[i]=a[i]*a[i];
fft(b,-1);
for(rg int i=0;i<len;i++) sum[i]=(ll)(b[i].r+0.5);
len=x[n]<<1;
for(rg int i=1;i<=n;i++) sum[x[i]+x[i]]--;
for(rg int i=1;i<=len;i++) sum[i]>>=1;
sum[0]=0;
for(rg int i=1;i<=len;i++) sum[i]+=sum[i-1];
for(rg int i=1;i<=n;i++)
{
ans+=sum[len]-sum[x[i]];
ans-=(ll)(n-i)*(i-1);ans-=n-1;ans-=(ll)(n-i)*(n-i-1)/2;
}
tot=(ll)n*(n-1)*(n-2)/6;
printf("%.7lf\n",1.0*ans/tot);
}
return 0;
}