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; }