题意:
有10^5条边,任选三条能组成三角形的概率?
题解:
因为边很多,所以要用FFT。
将边长变换后,对应元素自乘,再逆变换,就可以得到所有两条边和的方案数。但是由于同一条边可能选两次,所以要去掉这种情况。
用一个pre[i]来记两条边和大于i的方案数,枚举三角形的最长边,那么另外两条边的和必须比它大,但是这样算出来的方案数还包括了最长边也在另外两条边、最长边可能比另外两条边短的情况,所以要去掉。
代码写得好挫啊好挫啊……
//Time:2671ms
//Memory:8952KB
//Length:1788B
#include <iostream>
#include <cstdio>
#include <cmath>
#include <complex>
#include <cstring>
#define MAXN (4+(1<<18))
using namespace std;
typedef complex<long double> cmplx;
const double PI = acos(-1.0);
void FFT(cmplx y[],int n,double z)
{
for(int i(1),j(0); i<n; ++i)
{
for(int k=n>>1; k>(j^=k); k>>=1);
if(i<j) swap(y[i],y[j]);
}
for(int h(2); h<=n; h<<=1)
{
cmplx wn = exp(cmplx(0,z*2*PI/h));
for(int j(0); j<n; j+=h)
{
cmplx w(1,0);
for(int k(j); k<j+h/2; ++k)
{
cmplx u = y[k], t = w*y[k+h/2];
y[k] = u+t;
y[k+h/2] = u-t;
w*=wn;
}
}
}
if(z<0) for(int i(0); i<n; ++i) y[i]/=n;
}
int len[MAXN];
double pre[MAXN];
cmplx sum[MAXN];
int main()
{
//freopen("H:\\MyDocument\\Code\\input.txt","r",stdin);
int ncase,n,nn,mlen;
double ans;
scanf("%d",&ncase);
while(ncase--)
{
mlen=0;
scanf("%d",&n);
memset(sum,0,sizeof(sum));
for(int i=0;i<n;++i)
scanf("%d",&len[i]),sum[len[i]]+=1,mlen=max(mlen,len[i]);
nn=1;
mlen*=2;
while(nn<=mlen) nn<<=1;
FFT(sum,nn,1);
for(int i=0;i<=nn;++i) sum[i]*=sum[i];
FFT(sum,nn,-1);
ans=0;
for(int i=0;i<n;++i) sum[len[i]*2]-=1;
pre[0]=sum[0].real();
for(int i=1;i<=nn;++i)
pre[i]=pre[i-1]+sum[i].real();
for(int i=0;i<n;++i)
{
ans+=(pre[nn]-pre[len[i]])/2;
ans-=(long long)(n-i-1)*i+(n-1)+(long long)(n-i-1)*(n-i-2)/2;
}
printf("%.7f\n",ans/n/(n-1)/(n-2)*6);
}
return 0;
}