zoukankan      html  css  js  c++  java
  • HDU 4609 3idiots fft

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

      题目描述:给出n个数, 问其中选出三个能组成三角形的概率

      解题思路:给出kuangbin巨巨的解题思路, 实在是没有比这儿更好的题解, 所以我就不误导别人和自己了

      http://www.cnblogs.com/kuangbin/archive/2013/07/24/3210565.html

      题目代码:

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    
    const double PI = acos(-1.0);
    
    struct Complex {
        double x, y;
        Complex(double _x = 0.0, double _y = 0.0) {
            x = _x;
            y = _y;
        }
        Complex operator -(const Complex &b)const {
            return Complex(x-b.x, y-b.y);
        }
        Complex operator +(const Complex &b)const {
            return Complex(x+b.x, y+b.y);
        }
        Complex operator *(const Complex &b)const {
            return Complex(x*b.x-y*b.y, x*b.y+y*b.x);
        }
    };
    void change(Complex y[], int len) {
        int i, j, k;
    //    cout << len << endl;
    //    cout << "-----" << endl;
        for(i = 1, j = len/2; i < len-1; i++) {
            if( i < j ) swap(y[i], y[j]);
            k = len / 2;
            while( j >= k ) {
                j -= k;
                k /= 2;
    //            cout << "----" << endl;
    //            cout << j << " " << k << endl;
            }
            if( j < k ) j += k;
    //        cout << "==" << endl;
            
        }
    //    cout << "-----" << endl;
    //    cout << "-------------" << endl;
    }
    
    void fft(Complex y[], int len, int on) {
        
        change(y, len);
    //    cout << "----" << endl;
        for(int h = 2; h <= len; h <<= 1) {
            Complex wn(cos(-on*2*PI/h), sin(-on*2*PI/h));
            for(int j = 0; j < len; j+=h) {
                Complex w(1,0);
                for(int k = j; k < j+h/2; k++) {
                    Complex u = y[k];
                    Complex t = w*y[k+h/2];
                    y[k] = u+t;
                    y[k+h/2] = u-t;
                    w = w*wn;
                }
            }
        }
        if( on == -1 ) {
            for( int i = 0; i < len; i++ ) {
                y[i].x /= len;
            }
        }
    }
    
    const int MAXN = 400040;
    Complex x1[MAXN];
    int a[MAXN/4];
    long long num[MAXN];
    long long sum[MAXN];
    
    int main() {
        int t;
        int n;
        scanf( "%d", &t);
        while( t-- ) {
            scanf( "%d", &n );
            memset( num, 0, sizeof( num ) );
            for( int i = 0; i < n; i++ ) {
                scanf( "%d", &a[i] );
                num[a[i]]++; // 记录a[i]出现的次数
            }
    //        cout << "=====" << endl;
            sort(a, a+n);
            int len1 = a[n-1] + 1; // len 为最长边的界
            int len = 1;
            while( len < 2 * len1 ) len <<= 1;
            for( int i = 0; i < len1; i++ ) {
                x1[i] = Complex(num[i], 0);
            }
            for( int i = len1; i < len; i++ ) {
                x1[i] = Complex(0,0);
            }
    //        cout << "======" << endl;
            fft(x1, len, 1);
    //        cout << "-----" << endl;
            for( int i = 0; i < len; i++ ) {
                x1[i] = x1[i] * x1[i];
            }
            fft(x1, len, -1);
    //        cout << "-----" << endl;
            for( int i = 0; i < len; i++ ) {
                num[i] = (long long)(x1[i].x + 0.5);
            }
            len = 2 * a[n-1]; // 教程中 界为 2n 必须由 2n 个点来确定
            for(int i  = 0; i < n; i++ ) {
                num[a[i]+a[i]]--;   // 去重
            }
            for(int i = 1; i <= len; i++ ) {
                num[i] /= 2;   // 卷积后求得是排列数, 组合数除以2
            }
            sum[0] = 0;
            for( int i = 1; i <= len; i++ ) {
                sum[i] = sum[i-1] + num[i];
            }
            long long cnt = 0;
            for( int i = 0; i < n; i++ ) {
                cnt += sum[len] - sum[a[i]];  // 长度和 大于 a[i] 的个数
                cnt -= (long long)(n-1-i)*i; // 减去一大一小
                cnt -= (n-1); // 减去包括本身的
                cnt -= (long long)(n-1-i)*(n-2-i)/2; // 减去两个大的组合数
            }
            long long tot = (long long)n * (n-1) * (n-2) / 6;
    //        cout << cnt << "===" << tot << endl;
            printf( "%.7lf\n", (double)cnt / tot );
        }
        return 0;
    }
    View Code

      思考: 首先这道题要让我看到了fft 的一种应用, 那就是排列组合 , 如果遇到加和的组合的可以往fft的方向想, 因为如果a[k]x^k中, k表示 加数时, a[k]代表数量的话, 那么卷积后的a[k']x^k' 中的 加和 k' 的所有排列数位 a[k'] , 因为A^k1 * B^k2 = A^(k1+k2), 这个设计状态也很重要。

          以后要多做题, 多动脑子。

  • 相关阅读:
    vue-awesome-swiper 子项目内容高度适配问题
    ajax的原理及应用
    display:none opacity:0以及visibility:hidden的区别
    javascript 创建型设计模式
    圣杯布局和双飞翼布局
    javascript->对象继承
    js 宏任务和微任务
    android->按钮底部定位上移
    ios移动端开发的坑
    jvm系列 (五) ---类加载机制
  • 原文地址:https://www.cnblogs.com/FriskyPuppy/p/6806074.html
Copyright © 2011-2022 走看看