HDU 4609 3-idiots (FFT入门,帮助理解FFT模板)

http://acm.hdu.edu.cn/showproblem.php?pid=4609

3-idiots

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


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个线段,让你选其中三个,问你这三条线段能组成三角形的概率是多少?
思路:a+b<=c满足这样的关系一定不是三角形,用C(n,3)-不满足题意的即可;
那么你要先算出a+b的所有情况,暴力的话是O(n^2),所以用FFT加速O(nlogn);
FFT计算之后的a+b中有些是不符合题意的,比如a和b选的是同一条线段,还有选(a,b),跟选(b,a)重复了,
所以FFT计算过后的数组应该排除a+a这种情况,让后全部   除2.
最后计算不符合题意的三角形,枚举第三条边,小于等于这条边的(a+b)都算一次贡献
FFT讲解 :https://blog.csdn.net/ggn_2015/article/details/68922404
这篇写的应该是最清楚的了。

void getrev(int bit)//二进制反转
{
    for(int i=0; i<(1<<bit); i++)//模拟一下,能看懂的
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
}

void fft(cd* a,int n,int dft)
{
    for(int i=0; i<n; i++)
        if(i<rev[i])
            swap(a[i],a[rev[i]]);//二进制反转后交换数组存放的系数
    for(int step=1; step<n; step<<=1)//
    {
        cd wn=exp(cd(0,dft*PI/step));//单位wn
        for(int j=0; j<n; j+=step<<1)
        {
            cd wnk(1,0);//初始化为1
            for(int k=j; k<j+step; k++)//这下面的5条语句应该是最难懂的,我在纸上写了这几条语句的大概意思,具体意思还是要自己去分析
            {
                cd x=a[k];
                cd y=wnk*a[k+step];
                a[k]=x+y;
                a[k+step]=x-y;
                wnk*=wn;
            }
        }
    }
    if(dft==-1)
        for(int i=0; i<n; i++)
            a[i]/=n;
}

因为昨天才弄懂FFT,下面图片上的东西也是想了很久才想明白,所以发出来,可能会给你们灵感吧。

关于FFT的问题,我估计我也解决不了,大家行行好,就不要留言问我了(逃)
1,2,3,4,5,6,7,8只是我自己弄得一个编号,并不是A1,A2等系数;
蝴蝶操作:

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<complex>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef complex<double> cd;
#define LL long long
const int N=270000;//至少要比s大,270000>s>200000,s是一个二的次幂
const double PI=3.14159265358979;
cd a[N];
int c[N],d[N];
int rev[N];
void getrev(int bit)
{
    for(int i=0; i<(1<<bit); i++)
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
}

void fft(cd* a,int n,int dft)
{
    for(int i=0; i<n; i++)
        if(i<rev[i])
            swap(a[i],a[rev[i]]);
    for(int step=1; step<n; step<<=1)
    {
        cd wn=exp(cd(0,dft*PI/step));
        for(int j=0; j<n; j+=step<<1)
        {
            cd wnk(1,0);
            for(int k=j; k<j+step; k++)
            {
                cd x=a[k];
                cd y=wnk*a[k+step];
                a[k]=x+y;
                a[k+step]=x-y;
                wnk*=wn;
            }
        }
    }
    if(dft==-1)
        for(int i=0; i<n; i++)
            a[i]/=n;
}
LL output[N];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        mem(d,0);
        mem(a,0);
        int n;
        scanf("%d",&n);
        for(int i=0; i<n; i++)
        {
            scanf("%d",&c[i]);
            d[c[i]]++;
        }
        sort(c,c+n);
        int maxx=c[n-1];
        int w=maxx+maxx+1;
        int bit=1,s=2;
        for(bit=1; (1<<bit)<w; bit++)
            s<<=1;
        for(int i=0; i<=maxx; i++)
            a[i]=(double)d[i];
        getrev(bit);
        fft(a,s,1);
        for(int i=0; i<s; i++)a[i]=a[i]*a[i];
        fft(a,s,-1);
        for(int i=0; i<s; i++)
            output[i]=(LL)(a[i].real()+0.5);
        for(int i=0; i<n; i++)
            output[c[i]+c[i]]--;
        for(int i=0; i<s; i++)
            output[i]/=2;
        for(int i=1; i<s; i++)
            output[i]+=output[i-1];
        LL q=n*(LL)(n-1)*(n-2)/6;
        LL ans=q;
        for(int i=0; i<n; i++)
            ans-=output[c[i]];
        printf("%.7f\n",(double)ans/q);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xiangAccepted/article/details/81239353
今日推荐