zoukankan      html  css  js  c++  java
  • hdu4609(fft)

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

    题意: 给出 n 根木棒求从中任取三根可以组成三角形的概率.

    思路: fft

    将 a 数组转化为 num 数组, 其中 num[i] 为长度为 i 的木棒的数目, 再做 num 和 num 的卷积, 这步可以用 fft 模板完成. 将卷积结果存入 num 中, 则 num[i] 为从 n 根木棒中任选两根且其长度和为 i 的组合数. 注意: 里面包含了自己和自己组合以及正反序的情况. 所以要先对 num 处理一下.

    若两边的长和为 x (x > 0), 再有一条边长度小于 x 且大于 0, 那么这三条边一定可以组成三角形. 前面已经得到了 num 数组, 那么接下来可以枚举第三条边, 然后累计对答案的贡献. 为了避这里免重复计算, 先给 a 数组排序, 然后枚举 a[i] 作为当前三角形中的最长边, 累计对答案的贡献即可.

    得到了能组成三角形的方案数 sol, 则概率为 sol / (n * (n - 1) * (n - 2) / 6).

    代码:

      1 #include <iostream>
      2 #include <algorithm>
      3 #include <math.h>
      4 #include <string.h>
      5 #include <stdio.h>
      6 #define ll long long
      7 using namespace std;
      8 
      9 const double PI = acos(-1.0);
     10 
     11 struct Complex{//复数结构体
     12     double x, y;//实部,虚部
     13     Complex(double _x = 0.0, double _y = 0.0){
     14         x = _x;
     15         y = _y;
     16     }
     17     Complex operator -(const Complex &b) const{
     18         return Complex(x - b.x, y - b.y);
     19     }
     20     Complex operator +(const Complex &b) const{
     21         return Complex(x + b.x, y + b.y);
     22     }
     23     Complex operator *(const Complex &b) const{
     24         return Complex(x * b.x - y * b.y, x * b.y + y * b.x);
     25     }
     26 };
     27 
     28 //进行FFT和IFFT反转变化
     29 //位置i和(i二进制反转后位置)互换
     30 void change(Complex y[], int len){//len必须为2的幂
     31     for(int i = 1, j = len / 2; i < len - 1; i++){
     32         if(i < j) swap(y[i], y[j]); //交换互为下标反转的元素,i<j保证只交换一次
     33         int k = len >> 1;
     34         while(j >= k){
     35             j -= k;
     36             k /= 2;
     37         }
     38         if(j < k) j += k;
     39     }
     40 }
     41 
     42 //做FFT,len必须为2的幂,on=1是DFT,on=-1是IDTF
     43 void fft(Complex y[], int len, int on){
     44     change(y, len);//调用反转置换
     45     for(int h = 2; h <= len; h <<= 1){
     46         Complex wn(cos(-on * 2 * PI / h), sin(-on * 2 * PI / h));
     47         for(int j = 0; j < len; j += h){
     48             Complex w(1, 0);//初始化螺旋因子
     49             for(int k = j; k < j + h / 2; k++){//配对
     50                 Complex u = y[k];
     51                 Complex t = w * y[k + h / 2];
     52                 y[k] = u + t;
     53                 y[k + h / 2] = u - t;
     54                 w = w * wn;//更新螺旋因子
     55             }
     56         }
     57     }
     58     if(on == -1){
     59         for(int i = 0; i < len; i++){
     60             y[i].x /= len;//IDTF
     61         }
     62     }
     63 }
     64 
     65 const int MAXN = 4e5 + 10;
     66 ll sum[MAXN], num[MAXN];
     67 Complex x1[MAXN];
     68 int a[MAXN];
     69 
     70 int main(void){
     71     int t, n;
     72     scanf("%d", &t);
     73     while(t--){
     74         scanf("%d", &n);
     75         memset(num, 0, sizeof(num));
     76         memset(sum, 0, sizeof(sum));
     77         for(int i = 0; i < n; i++){
     78             scanf("%d", &a[i]);
     79             num[a[i]]++;
     80         }
     81         sort(a, a + n);
     82         int len = 1;
     83         int len1 = a[n - 1] + 1;
     84         while(len < (len1 << 1)) len <<= 1;
     85         for(int i = 0; i < len1; i++){
     86             x1[i] = Complex(num[i], 0);
     87         }
     88         for(int i = len1; i < len; i++){
     89             x1[i] = Complex(0, 0);
     90         }
     91         fft(x1, len, 1);
     92         for(int i = 0; i < len; i++){
     93             x1[i] = x1[i] * x1[i];
     94         }
     95         fft(x1, len, -1);//IDFT(x1*x1)
     96         for(int i = 0; i < len; i++){
     97             num[i] = (ll)(x1[i].x + 0.5);//四舍五入
     98         }
     99         //此时的num[i]为从a中选中任意两根木棒的和等于i的组合数,包含了自己和自己组合的情况以及正反序的组合情况
    100         ll sol = 0;
    101         len = a[n - 1] << 1;
    102         for(int i = 0; i < n; i++){//减去自己和自己组合的
    103             num[a[i] + a[i]]--;
    104         }
    105         for(int i = 0; i <= len; i++){//得到的组合数是无序的,所以要除2
    106             num[i] /= 2;
    107         }
    108         for(int i = 1; i <= len; i++){
    109             sum[i] += sum[i - 1] + num[i];//sum[i]即从n根木棍中选出两根长度和大于等于i的方案数
    110         }
    111         for(int i = 0; i < n; i++){//当a[i]为三角形最长边时对答案的贡献
    112             sol += sum[len] - sum[a[i]];
    113             sol -= (ll)(n - i - 1) * i;//减去有一条边大于a[i]的情况
    114             sol -= (ll)(n - i - 1) * (n - i - 2) / 2;//减去两条边都大于a[i]的情况
    115             sol -= n - 1;//减去包括a[i]的情况
    116         }
    117         ll cnt = (ll)n * (n - 1) * (n - 2) / 6;
    118         double gel = sol * 1.0 / cnt;
    119         printf("%.7lf
    ", gel);
    120     }
    121     return 0;
    122 }
    View Code
  • 相关阅读:
    IO 单个文件的多线程拷贝
    day30 进程 同步 异步 阻塞 非阻塞 并发 并行 创建进程 守护进程 僵尸进程与孤儿进程 互斥锁
    day31 进程间通讯,线程
    d29天 上传电影练习 UDP使用 ScketServer模块
    d28 scoket套接字 struct模块
    d27网络编程
    d24 反射,元类
    d23 多态,oop中常用的内置函数 类中常用内置函数
    d22 封装 property装饰器 接口 抽象类 鸭子类型
    d21天 继承
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7528191.html
Copyright © 2011-2022 走看看