HDU 4609 3-idiots FFT

http://acm.hdu.edu.cn/showproblem.php?pid=4609
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

题目大意:给出 n n n条线段的长度,问从中任取三条可以组成三角形的概率(每条线段只能取一次)。
思路: F F T FFT FFT,需要用到很巧妙的转换,推荐看 k u a n g b i n kuangbin kuangbin大佬的博客,讲的超级棒。https://www.cnblogs.com/kuangbin/archive/2013/07/24/3210565.html

#include<bits/stdc++.h>
using namespace std;        //FFT模板
typedef long long ll;

const int maxn=1e5+5;

struct Complex
{
    double x,y;
    Complex(double dx=0,double dy=0)
    {
        x=dx;
        y=dy;
    }
};

Complex operator +(Complex a,Complex b)
{
    return Complex(a.x+b.x,a.y+b.y);
}
Complex operator -(Complex a,Complex b)
{
    return Complex(a.x-b.x,a.y-b.y);
}
Complex operator *(Complex a,Complex b)
{
    return Complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}

const double pi=acos(-1.0); //PI
int limit=1,bit=0;
int wz[maxn<<2];
Complex a[maxn<<2];
int v[maxn],cnt[maxn];
ll num[maxn<<2],sum[maxn<<2];

void FFT(Complex *A,int inv)
{
    for(int i=0;i<limit;i++)
        if(i<wz[i])
            swap(A[i],A[wz[i]]);
    for(int mid=1;mid<limit;mid<<=1)
    {
        Complex wn(cos(pi/mid),inv*sin(pi/mid));
        for(int i=0;i<limit;i+=mid<<1)
        {
            Complex w(1,0);
            for(int j=0;j<mid;j++,w=w*wn)
            {
                Complex t1=A[i+j];
                Complex t2=w*A[i+mid+j];
                A[i+j]=t1+t2;
                A[i+mid+j]=t1-t2;
            }
        }
    }
}

int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(a,0,sizeof(a));
        memset(cnt,0,sizeof(cnt));
        limit=1,bit=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&v[i]);
            cnt[v[i]]++;
        }
        sort(v,v+n);
        int len=v[n-1];
        int tmp=len;
        len<<=1;
        while(limit<=len)
        {
            limit<<=1;
            bit++;
        }
        for(int i=0;i<limit;i++) //从低位到高位
        {
            if(i<=tmp)
                a[i].x=cnt[i];
            else
                a[i].x=0;
        }
        for(int i=0;i<limit;i++)
            wz[i]=(wz[i>>1]>>1)|((i&1)<<(bit-1));
        FFT(a,1);
        for(int i=0;i<limit;i++)
            a[i]=a[i]*a[i];
        FFT(a,-1);
        for(int i=0;i<limit;i++)
            num[i]=(ll)(a[i].x/limit+0.5);
        for(int i=0;i<n;i++)
            num[v[i]<<1]--; //选取同一根木棒两次 不合题意
        for(int i=0;i<limit;i++)
            num[i]>>=1; //选择是无序的 所以要除2
        for(int i=1;i<limit;i++)
            sum[i]=sum[i-1]+num[i];
        ll ans=0;
        for(int i=0;i<n;i++)//枚举三角形的最长边 v_k
        {
            ans+=sum[limit-1]-sum[v[i]]; // v_i + v_j > v_k 满足三角形性质
            ans-=(ll)(n-i-1)*i; // 一大一小 不合题意
            ans-=n-1;   // v_i || v_j 与 v_k 是同一根 不合题意
            ans-=(ll)(n-i-1)*(n-i-2)/2; //v_i >= v_k && v_j >= v_k 不合题意
        }
        ll tot=(ll)n*(n-1)*(n-2)/6;
        printf("%.7f\n",ans*1.0/tot);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值