• HDU 4609 FFT+组合数学


    3-idiots

    Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 7804    Accepted Submission(s): 2724


    Problem Description
    King OMeGa catched three men who had been streaking in the street. Looking as idiots though, the three men insisted that it was a kind of performance art, and begged the king to free them. Out of hatred to the real idiots, the king wanted to check if they were lying. The three men were sent to the king's forest, and each of them was asked to pick a branch one after another. If the three branches they bring back can form a triangle, their math ability would save them. Otherwise, they would be sent into jail.
    However, the three men were exactly idiots, and what they would do is only to pick the branches randomly. Certainly, they couldn't pick the same branch - but the one with the same length as another is available. Given the lengths of all branches in the forest, determine the probability that they would be saved.
     
    Input
    An integer T(T≤100) will exist in the first line of input, indicating the number of test cases.
    Each test case begins with the number of branches N(3≤N≤105).
    The following line contains N integers a_i (1≤a_i≤105), which denotes the length of each branch, respectively.
     
    Output
    Output the probability that their branches can form a triangle, in accuracy of 7 decimal places.
     
    Sample Input
    2
    4
    1 3 3 4
    4
    2 3 3 4
     
    Sample Output
    0.5000000
    1.0000000
     

    解析  问n个木棍任取三个能构成三角形的概率  毫无疑问一共有n*(n-1)*(n-2) / 6 种

        我们把木棍的长度计下数得到一个数组 num[ i ] 表示长度为i的木棍有多少个  第一组数据就是 num= { 0 , 1 , 0 , 2 , 1 } 

        我们将它看成多项式的系数  自身相乘一下得到{ 0 , 1 , 0 , 2 , 1 } *{ 0 , 1 , 0 , 2 , 1 } ={0 , 0 , 1 , 0 , 4 , 2 , 4 , 4 , 1 }=  新的num

    这个结果的意义如下:

    从{1 3 3 4}取一个数,从{1 3 3 4}再取一个数

    取两个数和为 2 的取法是一种:1+1

               和为 4 的取法有四种:1+3, 1+3  ,3+1 ,3+1

               和为 5 的取法有两种:1+4 ,4+1;

               和为 6的取法有四种:3+3,3+3,3+3,3+3,3+3

               和为 7 的取法有四种: 3+4,3+4,4+3,4+3

               和为 8 的取法有 一种:4+4

    其中有重复的 我们去重一下 

    首先 一根木棍只能用一次 所以和为 a[i]*2的方案数多算了一次  num[ a[i]*2] ] -1

    其次  1 3  3 1 是一个组合 但是算了两次 所以每一个num[ i ] / = 2  然后前缀和

    每个数的权值在【1,1e5】 FFT可以在nlogn时间内解决多项式乘法

    解决完上面的问题后  开始计数

    我没按照从小到大将木棍排序  然后枚举第 i 根木棍为能组成的三角形最长的那一根 那么两边之和大于第三边  满足的应该有这么多种 sum[ len] -sum[ a[i] ]  (len为和的最大值)

    但是里面有不满足的  

    1 再取的两个木棍 一个大于a[i] 一个小于a[i]  (大小指的是下标)

    2 再取的两个木棍都大于a[i]

    3 再取的两个木棍包含a[i]

    这样算一下就好了

    AC代码

    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<bits/stdc++.h>
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define all(a) (a).begin(), (a).end()
    #define fillchar(a, x) memset(a, x, sizeof(a))
    #define huan printf("
    ");
    using namespace std;
    typedef long long ll;
    typedef complex<double> complexd;
    const ll maxn=3e5+20,inf=0x3f3f3f3f;
    const ll mod=1e9+7;
    const double PI = acos(-1.0);
    /*
     * 进行FFT和IFFT前的反转变换。
     * 位置i和 (i二进制反转后位置)互换
     * len必须取2的幂
     */
    void change(complexd *y,int len)
    {
        int i,j,k;
        for(i = 1, j = len/2; i < len-1; i++)
        {
            if(i < j)
                swap(y[i],y[j]);
            //交换互为小标反转的元素,i<j保证交换一次
            //i做正常的+1,j左反转类型的+1,始终保持i和j是反转的
            k = len/2;
            while( j >= k)
            {
                j -= k;
                k /= 2;
            }
            if(j < k)
                j += k;
        }
    }
    /*
     * 做FFT
     * len必须为2^k形式,
     * on==1时是DFT,on==-1时是IDFT
     */
    void fft(complexd *y,int len,int on)
    {
        change(y,len);
        for(int h = 2; h <= len; h <<= 1)
        {
            complexd wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
            for(int j = 0; j < len; j+=h)
            {
                complexd w(1,0);
                for(int k = j; k < j+h/2; k++)
                {
                    complexd u = y[k];
                    complexd 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]=complexd(y[i].real()/len,y[i].imag());
    }
    complexd x[maxn];
    int a[maxn];
    ll num[maxn],sum[maxn];
    int main()
    {
        int t,n;
        scanf("%d",&t);
        while(t--)
        {
            fillchar(num,0);
            fillchar(sum,0);
            scanf("%d",&n);
            int maxx=-1;
            for(int i=0;i<n;i++)
            {
                scanf("%d",&a[i]);
                num[a[i]]++;
                maxx=max(maxx,a[i]);
            }
            int len = 1, len1 = maxx+1, len2 = maxx+1;
            while(len<len1+len2+1)
                len <<= 1;
            for(int i=0;i<=maxx;i++)
                x[i]=complexd((double)num[i],0);
            for(int i=maxx+1;i<len;i++)
                x[i]=complexd(0,0);
            fft(x, len, 1);
            for(int i = 0; i < len; i++)
                x[i] = x[i] * x[i];
            fft(x, len, -1);
            for(int i=0;i<len;i++)
                num[i]=(ll)(x[i].real()+0.5);
            sort(a,a+n);
            for(int i=0;i<n;i++)
                num[a[i]+a[i]]--;
            for(int i=1;i<len;i++)
                sum[i]=sum[i-1]+num[i]/2;
            ll ans=0;
            for(int i=0;i<n;i++)
            {
                ll temp=sum[len-1]-sum[a[i]];
                temp-=1ll*i*(n-i-1);   //一大一小 组合情况
                temp-=1ll*(n-i-1)*(n-i-2)/2; //两个大的 组合情况
                temp-=n-1;   //包含自己的情况
                ans+=temp;
            }
            printf("%.7f
    ",ans*6.0/((double)n*(n-1)*(n-2)));
        }
        return 0;
    }
  • 相关阅读:
    selenium 笔记 二次封装
    app测试点(转)
    git的使用
    ssm框架拦截器设置 & 改变数据库表结构 & JavaWeb出现Invalid bound statement (not found)
    mysql 错 Could not open JDBC Connection for transaction; nested exception is java.sql.SQLExceptio
    waitpid 中 的最后一个参数使用 WNOHANG && read和recv函数区别
    Ubuntu上安装make命令并使用makefile文件简化操作
    Ubuntu安装httpd(apache)及相关配置文件的作用
    QMYSQL driver not loaded 的解决办法 和 QDomDocument::setContent()
    操作系统知识点《一》
  • 原文地址:https://www.cnblogs.com/stranger-/p/9563091.html
Copyright © 2020-2023  润新知