HDU 4609 - 3-idiots
题意:
给出一些木棍的长度, 问你随机三根组成三角形的概率
分析:
枚举最长边,求出所有其余两边之和大于最长边的数目
将原来的长度点集 a[] 转化成某长度的数量点集 num[]
并求出任意两根相加的值的数量点集 sum[]
即 sum[m] = ∑num[i] * num[m - i] , 满足多项式相乘形式
故用 FFT 优化
然后 去掉 sum[] 中重复项 :
1. num[i]取了两次
2. num[i] * num[j] 与 num[j] * num[i] 重复
枚举最长边时, 要保持剩下两条边都比他小的性质
故要去掉不符项:
1. sum[] 中包含该边
2. 剩下两边中有一边大于该边
3. 剩下两边都大于该边
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 #define LL long long 8 const int MAXN = 1e5 + 5; 9 const int MAXM = 4e5 + 5; 10 const double PI = 4 * atan(1.0); 11 12 struct Complex 13 { 14 double x,y; 15 Complex(double xx = 0.0,double yy = 0.0): x(xx), y(yy) {} 16 Complex operator - (const Complex &b) const 17 { 18 return Complex(x - b.x, y - b.y); 19 } 20 Complex operator + (const Complex &b) const 21 { 22 return Complex(x + b.x, y + b.y); 23 } 24 Complex operator * (const Complex &b) const 25 { 26 return Complex(x*b.x - y*b.y, x*b.y + y*b.x); 27 } 28 }; 29 void Change(Complex y[], int len) 30 { 31 int i, j, k; 32 for (i = 1, j = len/2; i < len-1; i++) 33 { 34 if (i < j) swap(y[i], y[j]); 35 k = len/2; 36 while (j >= k) 37 { 38 j -= k; 39 k /= 2; 40 } 41 if (j < k) j += k; 42 } 43 } 44 void FFT(Complex y[], int len, int on) 45 { 46 Change(y, len); 47 for (int h = 2; h <= len; h <<= 1) 48 { 49 Complex wn( cos(-on * 2 * PI / h), sin(-on * 2 * PI / h) ); 50 for(int j = 0; j < len; j += h) 51 { 52 Complex w(1, 0); 53 for(int k = j; k < j + h/2; k++) 54 { 55 Complex u = y[k]; 56 Complex t = w * y[k + h/2]; 57 y[k] = u + t; 58 y[k + h/2] = u - t; 59 w = w * wn; 60 } 61 } 62 } 63 if(on == -1) 64 for(int i = 0; i < len; i++) 65 y[i].x /= len; 66 } 67 //以上模板 68 Complex x[MAXM]; 69 int a[MAXN]; 70 LL num[MAXM], sum[MAXN]; 71 int n, t; 72 LL ans; 73 int main() 74 { 75 scanf("%d", &t); 76 while (t--) 77 { 78 int m,len; 79 scanf("%d", &n); 80 memset(num,0,sizeof(num)); 81 for (int i = 0; i < n; i++) 82 { 83 scanf("%d", &a[i]); 84 num[a[i]]++; 85 } 86 sort(a,a+n); 87 m = a[n-1] + 1; 88 len = 1; 89 while(len < 2 * m) len <<= 1; 90 for(int i = 0; i < m; i++) x[i] = Complex(num[i], 0); 91 for(int i = m; i < len; i++) x[i] = Complex(0, 0); 92 FFT(x, len, 1); 93 for(int i = 0; i < len; i++) x[i] = x[i] * x[i]; 94 FFT(x, len, -1); 95 len = 2 *a[n-1]; 96 for(int i = 0; i <= len; i++) 97 sum[i] = (LL) (x[i].x + 0.5); 98 for(int i = 0; i < n; i++)//减去两个取一样的 99 sum[a[i] + a[i]]--; 100 for(int i = 0; i <= len; i++)//去掉重复 101 sum[i] /= 2; 102 for(int i = 1; i <= len; i++) 103 sum[i] += sum[i-1]; 104 ans = 0; 105 for(int i = 0; i < n; i++)//枚举最长边 106 { 107 LL cnt = sum[len] - sum[a[i]];//两边之和大于最长边 108 cnt -= (LL) (n - i - 1) * i;//一边大,一边小 109 cnt -= (LL) (n - i - 1) * (n - i - 2) / 2;//两边都大于最长边 110 cnt -= (n - 1);//选了自己 111 ans += cnt; 112 } 113 LL tot = (LL) n * (n - 1) * (n - 2) / 6; 114 printf("%.7f\n",(double)ans/tot); 115 } 116 }
我自倾杯,君且随意