hdu 4609 3-idiots


链接: http://acm.hdu.edu.cn/showproblem.php?pid=4609

题意: 给定 N 个正整数, 表示 N 条线段的长度, 问任取 3 条, 可以构成三角形的概率为多少~

数据范围: N<=10^5 ~~

思路:设三边分别为 x, y, z (x<=y<=z) 枚举 z ,统计 x+y 大于 z 的数目 .

比赛时能想到的只有 O(n^2) 的算法,无力 AC~

赛后才知道有种东西叫 FFT ~


以下为官方解题报告:

/*

  记录 A_i 为长度为 i 的树枝的数量,并让 A 对它本身做 FFT,得到任意选两个树枝能得到的各个和的数量。枚举第三边,

计算出所有两边之和大于第三条边的方案数,并把前两条边包含最长边的情况减掉就是答案。

*/

设长度为 a1,a2, ....an, 统计每种长度个数为 cnt[ ai ] ; 可表示多项式      

多项式自乘后, 其指数即为 ai + aj 的值, 系数即为 方案数, 减去不合法的即为所求~

  1 #include <cstdio>

  2 #include <cmath>

  3 #include <iostream>

  4 #include <algorithm>

  5 #include <cstring>

  6 using namespace std;

  7 typedef __int64 LL;

  8 const double pi=acos(-1);

  9 const int MAXN=3e5;

 10 const double eps=1e-6;

 11 struct C

 12 {

 13     double r, i;

 14     C (){}

 15     C(double _r, double _i):r(_r),i(_i){}

 16     inline C operator +(const C a)const{

 17     return C(r+a.r, i+a.i);    

 18     }

 19     inline C operator - (const C a)const {

 20         return C(r-a.r, i-a.i);    

 21     }

 22     inline C operator * (const C a)const{

 23         return C(r*a.r-i*a.i, r*a.i+i*a.r);    

 24     }

 25 }a[MAXN], b[MAXN];

 26 int num[MAXN], cnt[MAXN];

 27 LL res[MAXN], sum[MAXN];

 28 

 29 void brc(C *y,int l) // 二进制平摊反转置换 O(logn)

 30 {

 31     register int i,j,k;

 32     for(i=1,j=l>>1;i<l-1;i++){

 33         if(i<j)    swap(y[i],y[j]); // 交换互为下标反转的元素

 34                                 // i<j保证只交换一次

 35         k=l>>1;

 36         while(j>=k) {// 由最高位检索,遇1变0,遇0变1,跳出

 37             j-=k;

 38             k>>=1;

 39         }

 40         if(j<k)    j+=k;

 41     }

 42 }

 43 void FFT(C *y,int l,int on) // FFT O(nlogn)

 44                              // 其中on==1时为DFT,on==-1为IDFT

 45 {

 46     register int h,i,j,k;

 47     C u,t; 

 48     brc(y,l); // 调用反转置换

 49     for(h=2;h<=l;h<<=1) // 控制层数

 50     {

 51         // 初始化单位复根

 52         C wn(cos(on*2*pi/h),sin(on*2*pi/h));

 53         for(j=0;j<l;j+=h) // 控制起始下标

 54         {

 55             C w(1,0); // 初始化螺旋因子

 56             for(k=j;k<j+h/2;k++) // 配对

 57             {

 58                 u=y[k];

 59                 t=w*y[k+h/2];

 60                 y[k]=u+t;

 61                 y[k+h/2]=u-t;

 62                 w=w*wn; // 更新螺旋因子

 63             } // 据说上面的操作叫蝴蝶操作…

 64         }

 65     }

 66     if(on==-1)    for(i=0;i<l;i++)    y[i].r/=l; // IDFT

 67 }

 68 

 69 int n,  L , Max, T;

 70 int main() {

 71     scanf("%d", &T);

 72     while (T--) {

 73         Max = 0;

 74         memset(cnt, 0, sizeof(cnt));

 75         scanf("%d", &n);

 76         for (int i = 0; i < n; ++i) {

 77             scanf("%d", num + i);

 78             cnt[num[i]]++;

 79             Max = max(Max, num[i]);

 80         }

 81         ++Max;L = 1;

 82         while (L < Max <<1) {

 83             L <<= 1;

 84         }

 85         for (int i = 0; i < Max; ++i) {

 86             a[i] = C(cnt[i], 0);

 87         }

 88 

 89         for (int i = Max; i < L; ++i) {

 90             a[i] = C(0, 0);

 91         }

 92         FFT(a, L, 1);

 93         for (int i = 0; i < L; ++i)

 94             a[i] = a[i] * a[i]; // 多项式自乘

 95         FFT(a, L, -1);

 96 

 97         for (int i = 0; i < L; ++i) {

 98             res[i] = (LL) (a[i].r + 0.5);

 99         }

100 

101         for (int i = 0; i <= Max; ++i) 

102             res[i << 1] -= cnt[i];// 自己和自己乘, 即 枚举的是 x+x 的 

103     

104         for (int i = 0; i < L; ++i)

105             res[i] >>= 1; //  x+y 与  y+x 算一次 

106         for (int i = 1; i < L; ++i) {  //前缀和 

107             sum[i] = sum[i - 1] + res[i];     

108         }

109         double tot = 0, den =  1.0*n * (n - 1) * (n - 2)/6;

110         

111         for (int i = 0; i < n; ++i) {   

112             tot += sum[num[i]] / den;//  x+y<=z 的个数 

113         

114         }        

115         double ans = 1 - tot ;

116         printf("%.7f\n", ans);

117     }

118 

119     return 0;

120 }
View Code

 

你可能感兴趣的:(HDU)