bzoj3513
给定n个长度分别为$a_i$的木棒,问随机选择3个木棒能够拼成三角形的概率。
$a_i$和$n$都在$10^5$以内
对于每一个i,我们统计比i短的边有多少组合能组成长度<=i的
用1减去这个概率就是能拼成的概率
具体就是用sum[i]表示i出现的次数
sum[i]可以转化成如下卷积的样子
$$sum[i] = \sum_{j=1}^{i-1}sum[j] * sum[i - j - 1]$$
然后FFT
#include<bits/stdc++.h> #define LL long long using namespace std; const int maxn = 300010; const double pi = acos(-1.0); struct Cint { double r,i; Cint() {r = i = 0.00;} Cint(double _r,double _i) : r(_r),i(_i){} Cint operator + (const Cint &b)const{return Cint(r + b.r,i + b.i);} Cint operator - (const Cint &b)const{return Cint(r - b.r,i - b.i);} Cint operator * (const Cint &b)const{return Cint(r * b.r - i * b.i,i * b.r + r * b.i);} }s[maxn]; int a[maxn],LEN,n; LL sum[maxn],ans; inline void FFT_init(Cint *a,int len) { for(int i = 1,j = len >> 1,k;i < len - 1;i++) { if(i < j)swap(a[i],a[j]);k = len; while(j >= (k>>=1)) j -= k; if(j <= k) j += k; } } inline void FFT(Cint *a,int len,int f) { FFT_init(a,len);int l,i,j,k;Cint u,v; for(l = 2;l <= len;l <<= 1) { i = l >> 1; Cint w(cos(-f * 2 * pi / l),sin(-f * 2 * pi / l)); for(j = 0;j ^ len;j += l) { Cint wn(1.0,0.0); for(k = j;k ^ (i + j);k++) { u = a[k]; v = wn * a[i + k]; a[k] = u + v;a[k + i] = u - v; wn = w * wn; } } } if(f == -1) for(i = 0;i < len;i++)a[i] . r /= len; } int main() { //freopen("ou.txt","r",stdin); //freopen("x.txt","w",stdout); int T;scanf("%d",&T); while(T--) { scanf("%d",&n);int mx = 0;ans = 0; for(int i=0;i<=LEN + 1;i++)s[i] = Cint(0.0,0.0); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); mx = max(mx,a[i]); } for(LEN = 1;(LEN >> 1) < mx;LEN <<= 1); for(int i=1;i<=n;i++)s[a[i]].r += 1.0; FFT(s,LEN,1); for(int i=0;i<LEN;i++)s[i] = s[i] * s[i]; FFT(s,LEN,-1); for(int i=1;i<=n;i++)s[a[i] * 2].r -= 1.0; for(int i=1;i<=LEN;i++)sum[i] = sum[i-1] + floor(s[i].r + 0.5); for(int i=1;i<=n;i++)ans += sum[a[i]]; double pos = 3.0 * ans / n / (n - 1) / (n - 2); printf("%.7lf\n",1.0 - pos); } }