• hdu 4609 3-idiots <FFT>



    链接: 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
    ", ans);
    117     }
    118 
    119     return 0;
    120 }
    View Code
  • 相关阅读:
    hdu 1201 18岁生日
    线性代数
    关于pymongo的一些说明
    利用数组进行数据处理
    通用函数:快速的元素级数组函数
    IPython:一种交互式计算和开发环境
    关于一道面试题的思考
    wireshark中的抓包过滤器和显示过滤器
    wireshark推荐书籍
    wireshark 找不到网卡的解决办法
  • 原文地址:https://www.cnblogs.com/jian1573/p/3216865.html
Copyright © 2020-2023  润新知