HDU 4609

题意:

  给出一些木棍的长度, 问你随机三根组成三角形的概率

分析:

  枚举最长边,求出所有其余两边之和大于最长边的数目

  将原来的长度点集 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
",(double)ans/tot); 
115     }
116 }
我自倾杯,君且随意
原文地址:https://www.cnblogs.com/nicetomeetu/p/5750751.html