hdu 4609 3-idiots(FFT)

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≤10 5).
The following line contains N integers a_i (1≤a_i≤10 5), 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
 

Source
 

Recommend
liuyiding


题意:给你n个树枝,让你从中选三个,问你这三条树枝的长度能组成三角形的概率是多少。


思路:先用FFT把两条边的和的方法处理出来,然后枚举第三条边,让他当作最长边,记录总和。


求两条边的和,就是用这个长度数组和自己做卷积,把长度当成次数,这个长度出现的次数当成系数,这样多项式就出来了。

我们用num[i]来表示 前两条边组成长度为i的方案数

然而自己和自己求出的卷积,并不直接就是两条边组成的长度的方案数,还有重复的:

1、因为一条树枝只能选一次,所以自己和自己组成的方案要减掉,就是num[a[i] + a[i]] --, 0 <= i < n;

2、树枝的选择是无序的,所以先选a1,再选a2,和先选a2,再选a1是一样的方案,所以num[i] /= 2 , 1 <= i <= maxlen*2; 


我们先对num数组做一下前缀和,用sum数组保存,方便后面叙述。把最长能组合出来的长度设为len


然后就是枚举第三条边能选的长度,把他当成最长的那条边,因为三角形要保证两边之和大于第三边,(这里说的大小,是在数组里的位置关系,我们认为j > i的都是算作a[j] > a[i])所以能选的方案数就是sum[len] - sum[a[i]] ,然而这里也是有重复的:

1、前面两条边一个选的是a[i],一个选的是另外一条边,所以重复次数就是n-1

2、因为我们要保证a[i]是最长边,一条比a[i]长,一条比a[i]短的这种情况肯定不合法,重复次数是(n-i-1)*i   (下标是从0开始的)

3、同上,两条都比a[i]长的边肯定也是不合法的,重复次数 (n-i-1)*(n-i-2)/ 2


通过以上过程就能求出所有能构成三角形的方案数,然后求出总方案数:C(n,3) = n*(n-1)*(n-2) / 6;

相除就得到了概率。


#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#define LL long long
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;
}
const int MAXN = 400040;
complex x[MAXN];
LL sum[MAXN];
LL num[MAXN];
int a[MAXN/4];

int main(void)
{
    int T,n,i,j;
    scanf("%d",&T);
    while(T--)
    {
        memset(num,0,sizeof(num));
        scanf("%d",&n);
        for(i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            num[a[i]]++;
        }
        sort(a,a+n);
        int len = 1;
        while(len < (a[n-1]+1)*2)
            len <<= 1;
        for(i=0;i<len;i++)
            x[i] = complex(num[i],0);
        fft(x,len,1);
        for(i=0;i<len;i++)
            x[i] = x[i]*x[i];
        fft(x,len,-1);
        len = a[n-1]*2;
        for(i=1;i<=len;i++)
            num[i] = (LL)(x[i].r+0.5);

        for(i=0;i<n;i++)//去掉两个选同一个的重复
            num[a[i]+a[i]]--;

        for(i=1;i<=len;i++)//选择无序性
            num[i] /= 2;
        sum[0] = 0;
        for(i=1;i<=len;i++)
            sum[i] = sum[i-1] + num[i];

        LL cnt = 0;
        for(i=0;i<n;i++)
        {
            cnt += sum[len] - sum[a[i]];

            cnt -= (n-1); //本身和其他一个
            cnt -= (LL)(n-1-i)*i; // 一个比ai大,一个比ai小
            cnt -= (LL)(n-1-i)*(n-2-i)/2; //两个都比ai大
        }
        LL tot = (LL)n*(n-1)*(n-2)/6;
        double ans = 1.0*cnt/tot;
        printf("%.7f\n",ans);
    }

    return 0;
}


1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值