hdu 4609 3-idiots (快速傅里叶求卷积)

题意:给出n根木棒的长度,取三根木棒,求不取相同木棒且能构成三角形的概率;

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

思路:FFT的结果的意义为计算卷积;

卷积是指在两个集合中各取一个数,可能出现的各种数值的个数;(用每个集合中各种可能出现数值的个数,求各取一个数各种可能出现数值的个数);

得到取两个数的情况后,排除取相同木棒和顺序取棒的情况;

再枚举三边中的最长边,排除有不小于枚举边的情况;

一定要使用long long;

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define pi acos(-1.0)
int len1,len2;
long long a[500100],num[500100],sum[500100];
struct Complex{
    double r,i;
    Complex(){};
    Complex(double x,double y){
        r=x,i=y;
    }
    Complex operator +(const Complex &t)const{
       return Complex(r+t.r,i+t.i);
    }
    Complex operator -(const Complex &t)const{
       return Complex(r-t.r,i-t.i);
    }
    Complex operator *(const Complex &t)const{
       return Complex(r*t.r-i*t.i,r*t.i+i*t.r);
    }
}x1[500100];
void fft(Complex y[],int n,int rev)
{
    for(int i=1,j,k,t;i<n;i++){
       for(j=0,k=n>>1,t=i;k;k>>=1,t>>=1) j=j<<1|t&1;
       if(i<j) swap(y[i],y[j]);
    }
    for(int s=2,ds=1;s<=n;ds=s,s<<=1){
       Complex wn=Complex(cos(rev*2*pi/s),sin(rev*2*pi/s)),w=Complex(1,0),t;
       for(int k=0;k<ds;k++,w=w*wn){
          for(int i=k;i<n;i+=s){
              t=w*y[i+ds];
              y[i+ds]=y[i]-t;
              y[i]=y[i]+t;
          }
       }
    }
    if(rev==-1) for(int i=0;i<n;i++) y[i].r/=n;
}
long long temp,ans;
int main()
{
    int i,j,k,t,n,m;
    while(scanf("%d",&t)!=EOF)
    {
        while(t--)
        {
            memset(num,0,sizeof(num));
            memset(sum,0,sizeof(sum));
            ans=0;
            scanf("%d",&n);
            for(i=0;i<n;i++){
               scanf("%d",&a[i]);
               num[a[i]]++;
            }
            sort(a,a+n);
            len1=a[n-1];
            int len=1;
            while(len<=len1*2) len<<=1;
            for(i=0;i<=len1;i++) x1[i]=Complex(num[i],0);
            for(;i<len;i++) x1[i]=Complex(0,0); //扩展
            fft(x1,len,1); //DFT
            for(i=0;i<len;i++) x1[i]=x1[i]*x1[i]; //点乘
            fft(x1,len,-1); //IDFT
            for(i=0;i<len;i++)
                num[i]=(long long)(x1[i].r+0.5); //结果
            len=2*a[n-1];
            for(i=0;i<n;i++)
                num[a[i]*2]--;  //两根棒相同
            for(i=1;i<=len;i++)
                num[i]/=2;      //不同顺序只取一种
            for(i=1;i<=len;i++){
               sum[i]=sum[i-1]+num[i];
            }
            for(i=0;i<n;i++) //枚举三边中最长边
            {
                ans+=sum[len]-sum[a[i]]; //两边之和大于第三边
                ans-=(long long)(n-i-1)*i; //一大一小
                ans-=(n-1);  //一边与枚举边相同
                ans-=(long long)(n-i-1)*(n-i-2)/2; //两大
            }
            temp=(long long)n*(n-1)*(n-2)/6; //一定要long long
            printf("%.7f\n",(double)ans/temp);
        }
    }
    return 0;
}
时间: 2024-08-05 19:53:31

hdu 4609 3-idiots (快速傅里叶求卷积)的相关文章

hdu 4609 3-idiots 【FFT快(gui)速傅立叶变换】

FFT实现起来挺复杂的,开始用vector,结果发现空间超了,换成数组还是超,删掉了几个后又超时了 sin cos 函数调用次数太多了,改成乘法,还是超时 最后把FFT里的除法运算和模运算优化了一下,终于过了,排的老后面 坑,3843MS,一看时间最少的只有671MS,我都怀疑这是不是同一个算法..为毛差距这么大 #pragma comment(linker, "/STACK:102400000,102400000") #include<iostream> #include

hdu 3746 Cyclic Nacklace (KMP求最小循环节)

//len-next[len]为最小循环节的长度 # include <stdio.h> # include <algorithm> # include <string.h> using namespace std; int len; char a[100010]; int next[100010]; void Getnext() { int i=0,j=-1; next[0]=-1; while(i<=len) { if(j==-1||a[i]==a[j]) i

hdu 2262 高斯消元求期望

Where is the canteen Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 1070    Accepted Submission(s): 298 Problem Description After a long drastic struggle with himself, LL decide to go for some

HDU 4609 3-idiots FFT+容斥

一点吐槽:我看网上很多分析,都是在分析这个题的时候,讲了半天的FFT,其实我感觉更多的把FFT当工具用就好了 分析:这个题如果数据小,统计两个相加为 x 的个数这一步骤(这个步骤其实就是求卷积啊),完全可以母函数,无奈数据很大,就用FFT了 然后难点在于最后的统计,要减去自身,两个都大的,一大一小,包含自身,这是用到了容斥,再做相似的题的时候,应该多看看这方面 注:再次高度仰慕kuangbin神,这是我FFT的第二题,也是第二次用kuangbin FFT模板 #include <stdio.h>

HDU 4609 3-idiots(FFT)

题意:给出n个正整数(数组A).每次随机选出三个数.问这三个数能组成三角形的概率为多大? 首先,我们用类似桶排计数的方法作出两个数组a,b,储存每个长度有几条边,然后对两个数组求卷积. 求出卷积后,这就代表了2条边能构成的边长度的集合了,注意,由于求卷积的时候可能把两条相同的边相加,所以最后求出的数组一定要减去这重复的部分,然后,先算x后算y等价于先算y后算x,所以要除以二. 然后,对于第三条边a[i],我们这样考虑:令它作为这三条边中最大的那条! 所以之前的卷积求出来的两边和一定会比这条边大,

hdu 1008为何不对?求大神指导!

/*#include<iostream> using namespace std; int main(){ int n; while (cin >> n){ if (n == 0) break; else if (n > 0 && n < 100){ int sum=0, max,min; int *f = new int[n]; for (int i = 0; i < n; i++) cin >> f[i]; max = min =

hdu 3264 Open-air shopping malls 求两圆相交

对每个圆二分半径寻找可行的最小半径,然后取最小的一个半径. 对于两圆相交就只要求到两个扇形,然后减去两个全等三角形就行了. #include<cstdio> #include<iostream> #include<cmath> #include<algorithm> using namespace std; #define pi acos(-1.0) #define eps 1e-8 #define maxn 50 int n; struct point{

hdu 4418 高斯消元求期望

Time travel Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1480    Accepted Submission(s): 327 Problem Description Agent K is one of the greatest agents in a secret organization called Men in B

2014多校第五场1001 || HDU 4911 Inversion (归并求逆序数)

题目链接 题意 : 给你一个数列,可以随意交换两相邻元素,交换次数不超过k次,让你找出i < j 且ai > aj的(i,j)的对数最小是多少对. 思路 : 一开始想的很多,各种都想了,后来终于想出来这根本就是求逆序数嘛,可以用归并排序,也可以用树状数组,不过我们用树状数组做错了,也不知道为什么.求出逆序数来再减掉k次,就可以求出最终结果来了.求逆序数链接1,链接2 1 #include <stdio.h> 2 3 int left[250003], right[250003];