hdu 4609 3-idiots (FFT+计数)

3-idiots

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 2382    Accepted Submission(s): 822

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条边,问选出三条边能组成三角形的概率。

第一次搞FFT,了解了下卷积,具体实现是借鉴了别人的代码。

用num[i]表示长度为i的出现几次。对于样例1 3 3 4,我们得到num={0,1,0,2,1},

num数组和num数组卷积的含义:就是从{1 3 3 4}取一个数,从{1 3 3 4}再取一个

数,他们的和每个值各有多少个?

即{0 1 0 2 1}*{0 1 0 2 1} 卷积的结果应该是{0 0  1  0  4  2  4  4  1 }。

这个结果的意义如下:

从{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

<span style="font-size:14px;">#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#define ll long long
using namespace std;
const double pi=acos(-1.0);
const int maxn=100010;

struct Complex
{
    double a,b;
    Complex(){}
    Complex(double _a,double _b):a(_a),b(_b){}
    Complex operator + (const Complex &p)
    {
        return Complex(a+p.a,b+p.b);
    }
    Complex operator - (const Complex &p)
    {
        return Complex(a-p.a,b-p.b);
    }
    Complex operator * (const Complex &p)
    {
        return Complex(a*p.a-b*p.b,a*p.b+b*p.a);
    }
}s[maxn*4];

int n,len,cnt,a[maxn*4];
ll num[maxn*4],sum[maxn*4];

void initial()
{
    len=1;
    memset(num,0,sizeof(num));
    memset(sum,0,sizeof(sum));
}

void input()
{
    int co;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        num[a[i]]++;
    }
}

void ready()
{
    sort(a,a+n);
    int Max=a[n-1]+1;
    while(len<2*Max)  len<<=1;
    for(int i=0;i<Max;i++)    s[i]=Complex(num[i],0);
    for(int i=Max;i<len;i++)  s[i]=Complex(0,0);
}

void change()
{
     for(int i=1,j=len/2;i<len-1;i++)
     {
         if(i<j)  swap(s[i],s[j]);
         int k=len/2;
         while(j>=k)
         {
             j-=k;
             k/=2;
         }
         if(j<k)  j+=k;
     }
}

void FFT(int on)
{
    change();
    for(int i=2;i<=len;i<<=1)
    {
        Complex wn=Complex(cos(-on*2*pi/i),sin(-on*2*pi/i));
        for(int j=0;j<len;j+=i)
        {
            Complex w(1,0);
            for(int k=j;k<j+i/2;k++)
            {
                Complex u=s[k];
                Complex t=w*s[k+i/2];
                s[k]=u+t;
                s[k+i/2]=u-t;
                w=w*wn;
            }
        }
    }
    if(on==-1)
    {
        for(int i=0;i<len;i++)
            s[i].a/=len;
    }
}

void deal()
{
    FFT(1);
    for(int i=0;i<len;i++)  s[i]=s[i]*s[i];
    FFT(-1);
    cnt=2*a[n-1];
    for(int i=0;i<=cnt;i++)  num[i]=(ll)(s[i].a+0.5);
}

void solve()
{
    ll ans=0;
    for(int i=0;i<n;i++)    num[a[i]+a[i]]--;          //  减去两次取相同的
    for(int i=1;i<=cnt;i++) num[i]/=2;                //   对于两次去不同的算了两边,去重
    for(int i=1;i<=cnt;i++)  sum[i]=sum[i-1]+num[i];

    for(int i=0;i<n;i++)
    {
         ll t=sum[cnt]-sum[a[i]];
         ll b=n-1;                     //  减去与a[i]组合的
         ll c=(ll)i*(n-i-1);           //  减去一大一小组合的
         ll d=(ll)(n-i-1)*(n-i-2)/2;   //  减去两大组合的
         ans+=t-b-c-d;
    }
    ll mul=(ll)(n)*(n-1)*(n-2)/6;
    printf("%.7lf\n",ans*1.0/mul);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        initial();
        input();
        ready();
        deal();
        solve();
    }
    return 0;
}</span>
时间: 2024-10-12 05:44:27

hdu 4609 3-idiots (FFT+计数)的相关文章

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

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

HDU 4609 3-idiots(FFT)

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

HDU 4609 3-idiots ——(FFT)

这是我接触的第一个关于FFT的题目,留个模板. 这题的题解见:http://www.cnblogs.com/kuangbin/archive/2013/07/24/3210565.html. FFT的模板如下: 1 #include<bits/stdc++.h> 2 using namespace std; 3 const double pi = atan(1.0)*4; 4 struct Complex { 5 double x,y; 6 Complex(double _x=0,double

HDU 4609 3-idiots(FFT计数)

题意:给n(n<=100000)根棍子.每根棍子的长度是m(m<=100000),求从中任意取出三根的概率: 题解:经典FFT计数...枚举最长边..然后经过一系列玄学取重就可以啦..好神奇呀..细节见代码. #include<bits/stdc++.h> using namespace std; #define pie acos(-1.0) const int maxn = 4e5 + 10; typedef long long ll; struct Cp{double x,y;

HDU 4609 3-idiots 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> #

【FFT】HDU 4609 3-idiots

通道:http://acm.hdu.edu.cn/showproblem.php?pid=4609 题意:n条边长,求任选3条能组成三角形的概率. 思路:求出res后,减去一大一下,减去都大于它的边,减去该边和其他边组成的方案. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 6 const int N =400007; 7 8

hdu 4609 3-idiots —— FFT

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4609 算不合法的比较方便: 枚举最大的边,每种情况算了2次,而全排列算了6次,所以还要乘3: 注意枚举最大边的范围是 mx 而不是 lim !!否则会超过开的数组范围!!! 代码如下: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath

FFT(快速傅里叶变换):HDU 4609 3-idiots

3-idiots Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3560    Accepted Submission(s): 1241 Problem Description King OMeGa catched three men who had been streaking in the street. Looking as i

HDU 4609 3-idiots FFT+容斥

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