[MUTC2013][bzoj3513] idiots [FFT]
题面
思路
首先有一个容斥原理的结论:可以组成三角形的三元组数量=所有三元组-不能组成三角形的三元组
也就是说我们只要求出所有不能组成三角形的三元组即可
我们考虑三元组(a,b,c),a<=b<=c,其不能组成三元组的条件是a+b<=c
然后,这道题中并没有顺序限制
于是我们考虑用sum[i]表示长度为i的木棍的个数
将sum[i]为$x^i$的系数的多项式自乘,得到一个2*n项的多项式
那么新多项式(设为S)的第i项系数S[i]就代表着选择总和为i的两条边的方法数量
注意这个S[i]并不就是最后的答案,因为如果i是偶数,那么原来长度为i/2的边会被算两遍,所以要减去sum[i/2]
同时,这样处理以后的所有S[i]还要除二,因为会出现(i,j)(j,i)重复计算的情况
最后,我们维护sum的后缀和suf,答案就是$\sum_{i=1}{maxlen}S[i]suf[i]$了
不要忘记用所有三元组的数量减掉这个答案
Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
struct complex{
double x,y;
complex(double xx=0,double yy=0){x=xx;y=yy;}
complex operator +(const complex &b){return complex(b.x+x,b.y+y);}
complex operator -(const complex &b){return complex(-b.x+x,-b.y+y);}
complex operator *(const complex &b){return complex(x*b.x-y*b.y,x*b.y+y*b.x);}
}A[600010];
const double pi=acos(-1.0);
int n,sum[300010],limit=1,cnt=0,m,suf[300010],r[600010],maxn;
void fft(complex *a,double type){
int i,mid,j,k,R;complex w,wn,x,y;
for(i=0;i<limit;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(mid=1;mid<limit;mid<<=1){
wn=complex(cos(pi/mid),type*sin(pi/mid));
for(R=mid<<1,j=0;j<limit;j+=R){
w=complex(1,0);
for(k=0;k<mid;k++,w=w*wn){
x=a[j+k];y=w*a[j+k+mid];
a[j+k]=x+y;
a[j+k+mid]=x-y;
}
}
}
}
ll f[300010];
int main(){
int T=read(),i,t1;ll ans,tot;
while(T--){
memset(suf,0,sizeof(suf));memset(sum,0,sizeof(sum));
n=read();m=0;limit=1;cnt=0;memset(r,0,sizeof(r));
memset(A,0,sizeof(A));ans=tot=(ll)n*(ll)(n-1)*(ll)(n-2)/6ll;
for(i=1;i<=n;i++) t1=read(),sum[t1]++,A[t1].x++,m=max(m,t1);
for(i=m;i>=1;i--) suf[i]=suf[i+1]+sum[i];
maxn=m<<1;
while(limit<=maxn) limit<<=1,cnt++;
for(i=0;i<limit;i++) r[i]=((r[i>>1]>>1)|((i&1)<<(cnt-1)));
fft(A,1);
for(i=0;i<=limit;i++) A[i]=A[i]*A[i];
fft(A,-1);
for(i=0;i<=limit;i++){
f[i]=(ll)(A[i].x/limit+0.5);
if(!(i&1)) f[i]-=(ll)(sum[i>>1]);
f[i]>>=1;
}
for(i=0;i<=limit;i++) ans-=f[i]*(ll)suf[i];
printf("%.7lf\n",(double)ans/(double)tot);
}
}