【bzoj3513】[MUTC2013]idiots FFT
题目描述
给定n个长度分别为a_i的木棒,问随机选择3个木棒能够拼成三角形的概率。
输入
第一行T(T<=100),表示数据组数。
接下来若干行描述T组数据,每组数据第一行是n,接下来一行有n个数表示a_i。
3≤N≤10^5,1≤a_i≤10^5
输出
T行,每行一个整数,四舍五入保留7位小数。
样例输入
2
4
1 3 3 4
4
2 3 3 4
样例输出
0.5000000
1.0000000
题解
FFT
考虑什么样的3根木棍不能构成三角形:最长边大于等于其余两边之和。
因为长度只有$10^5$,因此可以直接记录由两根木棒拼成某长度的方案数,然后直接求前缀和统计答案即可。
但是朴素的统计方案数的时间复杂度是$O(n^2)$的,会TLE。
考虑到两边的长度s2[]和一边的长度s1[]的卷积有关,因此可以先使用FFT求某长度的个数s1[]的卷积,然后由于两根相同的木棒统计到了答案中,需要减掉;其余的方案出现了2次,需要再除以2.
最后求前缀和统计答案即可。注意需要long long。
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 using namespace std; typedef long long ll; const int len = 262144; const double pi = acos(-1); struct data { double x , y; data() {} data(double x0 , double y0) {x = x0 , y = y0;} data operator+(const data &a)const {return data(x + a.x , y + a.y);} data operator-(const data &a)const {return data(x - a.x , y - a.y);} data operator*(const data &a)const {return data(x * a.x - y * a.y , x * a.y + y * a.x);} }a[N << 2]; int w[N]; ll sum[N << 2]; void fft(int flag) { int i , j , k; for(i = k = 0 ; i < len ; i ++ ) { if(i > k) swap(a[i] , a[k]); for(j = len >> 1 ; (k ^= j) < j ; j >>= 1); } for(k = 2 ; k <= len ; k <<= 1) { data wn(cos(2 * pi * flag / k) , sin(2 * pi * flag / k)); for(i = 0 ; i < len ; i += k) { data w(1 , 0) , t; for(j = i ; j < i + (k >> 1) ; j ++ , w = w * wn) t = w * a[j + (k >> 1)] , a[j + (k >> 1)] = a[j] - t , a[j] = a[j] + t; } } } void work() { int i; fft(1); for(i = 0 ; i < len ; i ++ ) a[i] = a[i] * a[i]; fft(-1); for(i = 0 ; i < len ; i ++ ) a[i].x /= len; } int main() { int T; scanf("%d" , &T); while(T -- ) { memset(a , 0 , sizeof(a)); int n , i; ll ans = 0; scanf("%d" , &n); for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &w[i]) , a[w[i]].x ++ ; work(); for(i = 1 ; i <= n ; i ++ ) a[w[i] * 2].x -- ; for(i = 1 ; i < len ; i ++ ) sum[i] = sum[i - 1] + (ll)(a[i].x / 2 + 0.1); for(i = 1 ; i <= n ; i ++ ) ans += sum[w[i]]; printf("%.7Lf\n" , 1 - (long double)ans / ((long double)n * (n - 1) * (n - 2) / 6)); } return 0; }