HDU 4609 3-idiots [FFT]

来源:互联网 发布:淘宝洗车工具大全 编辑:程序博客网 时间:2024/06/08 10:41

题意:给你n个数,求从其中取出3个数能成为三角形的概率

题解:既然要做三角形,所以我们需要,知道两边和的大小,但是两边和的大小需要n^2,所以需要FFT加速卷积计算两边和的情况,我们用num数组记录数字x出现的次数。要减去同一个数用两次的情况,和A与B B与A的情况。

计算出所有两边和的情况后,我们只要枚举每个数,按照枚举第三边大于其他两边的情况就不会重复,所以我们找到所有大于a[i]的两边之和的情况,减去其余边一大一小的情况,其余边都大于a[i]的情况,其中有一条是a[i]的情况,就是答案。(要注意num一共10000*10000总情况会爆int)

AC代码:

#include<stdio.h>#include<string.h>#include<iostream>#include<algorithm>#include<math.h>#define MAXN 400040typedef long long ll;using namespace std;const double PI = acos(-1.0);//复数结构体struct complex{    double r,i;    complex(double _r = 0.0,double _i = 0.0)    {        r = _r; i = _i;    }    complex operator +(const complex &b)    {        return complex(r+b.r,i+b.i);    }    complex operator -(const complex &b)    {        return complex(r-b.r,i-b.i);    }    complex operator *(const complex &b)    {        return complex(r*b.r-i*b.i,r*b.i+i*b.r);    }};/* * 进行FFT和IFFT前的反转变换。 * 位置i和 (i二进制反转后位置)互换 * len必须取2的幂 */void change(complex 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(complex y[],int len,int on){    change(y,len);    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].r /= len;}complex x1[MAXN],x2[MAXN];ll num[MAXN],qian[MAXN];int a[MAXN];int main(){int T;scanf("%d",&T);while(T--){memset(num,0,sizeof(num));ll n;scanf("%lld",&n);int ma=0;for(int i=0;i<n;i++){scanf("%d",&a[i]);num[a[i]]++;ma=max(ma,a[i]);}sort(a,a+n);ma++;int len=1;while(len<ma*2)len<<=1;         for(int i=0;i<ma;i++)x1[i]=complex(num[i],0);for(int i=ma;i<len;i++)x1[i]=complex(0,0);      for(int i=0;i<ma;i++)x2[i]=complex(num[i],0);for(int i=ma;i<len;i++)x2[i]=complex(0,0);//求DFTfft(x1,len,1);fft(x2,len,1);for(int i=0;i<len;i++)x1[i]=x1[i]*x2[i];fft(x1,len,-1);for(int i=0;i<len;i++)num[i]=(ll)(x1[i].r+0.5);len=(ma-1)*2;for(int i=0;i<n;i++)//去除本身与本身组成的长度 num[a[i]+a[i]]--;for(int i=1;i<=len;i++)//去除A+B或者B+A的重复情况 num[i]/=2;qian[0]=0;for(int i=1;i<=len;i++)qian[i]=qian[i-1]+num[i];ll cnt=0;for(int i=0;i<n;i++){cnt+=qian[len]-qian[a[i]]; //所有符合的种类 cnt-=(ll)(i)*(n-1-i); //一小一大的情况 cnt-=(n-1); //取到本身的情况 cnt-=(ll)(n-i-1)*(n-i-2)/2;  //取到比他大的情况 C(big,2);}ll all=n*(n-1)*(n-2)/6;printf("%.7f\n",(double)cnt/all);}return 0;}


原创粉丝点击