2 4 1 3 3 4 4 2 3 3 4
0.5000000 1.0000000
题意:给你n个边,求从其中选出3个组成三角形的概率
思路:参考-kuangbin大神
如果我们用num[i]表示长度为i的木棍有多少个,对于1 3 3 4就是
num[] = {0 1 0 2 1}从卷积的公式来看
乘法第k位置上的值 便是a[i]*b[j](i + j == k),如果位置表示的是长度,num[]表示的个数,那么卷积过后我们得到的便是两边和的个数
{0 1 0 2 1}*{0 1 0 2 1} 卷积的结果应该是{0 0 1 0 4 2 4 4 1 }。
在求出了两条边的和后,枚举第三边
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; typedef long double ld; const ld eps=1e-10; const int inf = 0x3f3f3f; const int MOD = 1e9+7; const double PI = acos(-1.0); struct Complex { double x,y; Complex(double _x = 0.0,double _y = 0.0) { x = _x; y = _y; } Complex operator-(const Complex &b)const { return Complex(x-b.x,y-b.y); } Complex operator+(const Complex &b)const { return Complex(x+b.x,y+b.y); } Complex operator*(const Complex &b)const { return Complex(x*b.x-y*b.y,x*b.y+y*b.x); } }; 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]); k = len/2; while(j >= k) { j-=k; k/=2; } if(j < k) j+=k; } } 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].x /= len; } } const int maxn = 401000; Complex x1[maxn],x2[maxn]; ll sum[maxn]; ll num[maxn]; int a[maxn]; int main() { int T; int n; scanf("%d",&T); while(T--) { scanf("%d",&n); int len = 1; int len1; memset(num,0,sizeof(num)); for(int i = 0; i < n; i++) { scanf("%d",&a[i]); num[a[i]]++; } sort(a,a+n); len1 = a[n-1] + 1; 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); fft(x1,len,1); //fft(x2,len,1); for(int i = 0; i < len; i++) { x1[i] =x1[i]*x1[i]; //cout << x1[i].x << " "<< x1[i].y <<endl; } fft(x1,len,-1); for(int i = 0; i < len; i++) { sum[i] = (ll)(x1[i].x+0.5); //cout << sum[i] << endl; } len = a[n-1] * 2; for(int i = 0; i < n; i++) sum[a[i]+a[i]] --; for(int i = 1; i <= len; i++) sum[i] /= 2; for(int i = 1; i <= len; i++) { sum[i] += sum[i-1]; } ll tot = (ll)n*(n-1)*(n-2)/6; ll ans = 0; for(int i = 0; i < n; i++) { ans += sum[len]-sum[a[i]]; //两边之和大于第三边 ans -= (ll)(n-1-i) * i; //一个比自己大,一个比自己小 ans -= (n-1); //取了自己 ans -= (ll)(n-1-i)*(n-2-i)/2; //都比自己大 } //printf("%.7lf\n",(double)ans/tot); printf("%.7f\n",(double)ans/tot); } return 0; }