题意:
给出n根棍子,让你求出可以组合成三角形的方法数
题解:
先任意选择两根棍子组合,这个用FFT即可
这里需要注意:
去重:选了两次本身,以及选了相同的组合两次
sort(a,a+n); len=2*a[n-1];///缩小区间 for(int i=0;i<n;i++)///去掉本身选两次的情况 num[a[i]+a[i]]--; for(int i=1;i<=len;i++)///选择的无序,除以2 num[i]/=2;对于第三根棍子,我们先将棍子长度排好序:
然后从小到大进行选择:
每次选择的棍子当做一个三角形里面长度最短的,因为是枚举每一根棍子,所以可以覆盖所有的情况
同时,也要去掉错误的情况
LL cnt=0; for(int i=0;i<n;i++) { cnt+=sum[len]-sum[a[i]];///两两组合的前缀和之差 cnt-=(LL)(n-1-i)*i;///减掉一个取大,一个取小的 cnt-=(n-1);///减掉一个取本身,另外一个取其它 cnt-=(LL)i*(i-1)/2;///减掉小于它的取两个的组合 }
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <math.h> using namespace std; const double PI = acos(-1.0); //复数结构体 struct complex { double r,i; complex(double _r = 0.0,double _i = 0.0) { r = _r; i = _i; } complex operator +(const complex &b) { return complex(r+b.r,i+b.i); } complex operator -(const complex &b) { return complex(r-b.r,i-b.i); } complex operator *(const complex &b) { return complex(r*b.r-i*b.i,r*b.i+i*b.r); } }; /* * 进行FFT和IFFT前的反转变换。 * 位置i和 (i二进制反转后位置)互换 * len必须去2的幂 */ void change(complex y[],int len) { int i,j,k; for(i = 1, j = len/2;i < len-1; i++) { if(i < j)swap(y[i],y[j]); //交换互为小标反转的元素,i<j保证交换一次 //i做正常的+1,j左反转类型的+1,始终保持i和j是反转的 k = len/2; while( j >= k) { j -= k; k /= 2; } if(j < k) j += k; } } /* * 做FFT * len必须为2^k形式, * on==1时是DFT,on==-1时是IDFT */ void fft(complex y[],int len,int on) { change(y,len); for(int h = 2; h <= len; h <<= 1) { complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h)); for(int j = 0;j < len;j+=h) { complex w(1,0); for(int k = j;k < j+h/2;k++) { complex u = y[k]; complex t = w*y[k+h/2]; y[k] = u+t; y[k+h/2] = u-t; w = w*wn; } } } if(on == -1) for(int i = 0;i < len;i++) y[i].r /= len; } #define LL long long const int MAXN = 200010*2; complex x1[MAXN]; int a[MAXN/4]; LL sum[MAXN],num[MAXN]; int main() { int n,m,T; //freopen("in.txt","r",stdin); scanf("%d",&T); while(T--) { scanf("%d",&n); int len1=-1,len=1; memset(num,0,sizeof(num)); for(int i=0;i<n;i++){ scanf("%d",&a[i]); len1=len1>a[i]?len1:a[i]; num[a[i]]++; } len1++; while(len < len1*2) len<<=1; for(int i = 0;i < len1;i++) x1[i] = complex(num[i],0); for(int i = len1;i < len;i++) x1[i] = complex(0,0); //求DFT fft(x1,len,1); for(int i = 0;i < len;i++) x1[i] = x1[i]*x1[i]; fft(x1,len,-1); for(int i = 0;i < len;i++) num[i] = (LL)(x1[i].r+0.5); sort(a,a+n); len=2*a[n-1];///缩小区间 for(int i=0;i<n;i++)///去掉本身选两次的情况 num[a[i]+a[i]]--; for(int i=1;i<=len;i++)///选择的无序,除以2 num[i]/=2; sum[0]=0;///前缀和 for(int i=1;i<=len;i++) sum[i]=sum[i-1]+num[i]; LL cnt=0; for(int i=0;i<n;i++) { cnt+=sum[len]-sum[a[i]]; cnt-=(LL)(n-1-i)*i;///减掉一个取大,一个取小的 cnt-=(n-1);///减掉一个取本身,另外一个取其它 cnt-=(LL)i*(i-1)/2;///减掉小于它的取两个的组合 } LL tot=(LL)n*(n-1)*(n-2)/6; printf("%.7lf\n",(double)cnt/tot); } return 0; }