zoukankan      html  css  js  c++  java
  • HDU#4609. 3-idiots


    题目大意:

    给定n个长度为ai的木棍
    求,任选三个使其能够组成三角形的概率



    分析:

    ai <= 1e5,先统计c[i]代表长度为i的木棍有多少个
    对c做卷积,即使用FFT求出 rep(i,0,1e5) rep(j,0,1e5) d[i+j]+=c[i]*c[j] 的d数组
    d[i]即代表选出两个木棍长度之和为i的方案数
    去除重复,减去使用了同一木棍两次的,i+j与j+i应为同一方案,所以rep(i,0,2e5) d[i]/=2
    现在d[i]已经可以表示选出两个不同木棍长度之和为i的不同方案数了

    接下来对木棍长度ai从小到大排序,对d做前缀和sd
    枚举最长边为第i根木棍,则另外两条边长度之和>ai,ans+=sd[2e5]-sd[ai]
    这些方案里需要去除一些不满足要求(ai为最长边)的
    1.另外两条边两条均>ai,ans-=(n-i)*(n-i-1)/2
    2.另外两条边一条>ai,一条<ai,ans-=(n-i)*(i-1)
    3.另外两条边一条=ai,另一条随意,ans-=n-1


    附上代码:

    /*     ai <= 1e5,先统计c[i]代表长度为i的木棍有多少个
         对c做卷积,即使用FFT求出 rep(i,0,1e5) rep(j,0,1e5) d[i+j]+=c[i]*c[j] 的d数组
         d[i]即代表选出两个木棍长度之和为i的方案数
         去除重复,减去使用了同一木棍两次的,i+j与j+i应为同一方案,所以rep(i,0,2e5) d[i]/=2
         现在d[i]已经可以表示选出两个不同木棍长度之和为i的不同方案数了
     
         接下来对木棍长度ai从小到大排序,对d做前缀和sd
         枚举最长边为第i根木棍,则另外两条边长度之和>ai,ans+=sd[2e5]-sd[ai]
         这些方案里需要去除一些不满足要求(ai为最长边)的
         1.另外两条边两条均>ai,ans-=(n-i)*(n-i-1)/2
         2.另外两条边一条>ai,一条<ai,ans-=(n-i)*(i-1)
         3.另外两条边一条=ai,另一条随意,ans-=n-1*/
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=4e5+12;
    const double pi=acos(-1);
    int n,N;
    long long num[maxn],branch[maxn];
    
    struct Complex
    {
        double x,i;
        Complex(){}
        Complex(double a,double b) {x=a;i=b;}
    }A[maxn];
    Complex operator + (Complex a,Complex b) {return Complex(a.x+b.x,a.i+b.i);}
    Complex operator - (Complex a,Complex b) {return Complex(a.x-b.x,a.i-b.i);}
    Complex operator * (Complex a,Complex b) {return Complex(a.x*b.x-a.i*b.i,a.x*b.i+a.i*b.x);}
    
    int rev[maxn];
    void FFT(Complex *a,int t)
    {
        for(int i=0;i<n;i++) if(rev[i]>i) swap(a[i],a[rev[i]]);
    
        for(int i=1;i<n;i<<=1)
        {
            Complex wn(cos(2*pi/(i<<1)),t*sin(2*pi/(i<<1)));
            for(int j=0;j<n;j+=(i<<1))
            {
                Complex w(1,0),t0,t1;
                for(int k=0;k<i;k++) 
                {
                    t0=a[j+k];t1=w*a[i+j+k];
                    a[j+k]=t0+t1;
                    a[i+j+k]=t0-t1;
                    w=w*wn;
                }
            }
        }
    }
    long long sum[maxn];
    int main()
    {
        freopen("a.in","r",stdin);
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&N);
            memset(num,0,sizeof(num));
            for(int i=0;i<N;i++) scanf("%d",&branch[i]),num[branch[i]]++;
            sort(branch,branch+N);//从小到大排序 
            
            int imax=branch[N-1]+1;//最大的数     
            n=1;int len=0;//n为2的len次方 n要比2*imax-1大 避免冲突 
            while(n<imax*2) n<<=1,len++;//两个imax相乘 会更新2*imax的位置 
            
            for(int i=0;i<imax;i++) A[i]=Complex(num[i],0);// 
            for(int i=imax;i<n;i++) A[i]=Complex(0,0);
            
            rev[0]=0;
            for(int i=1;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));//len是要他们的二进制长度+1 
        
            FFT(A,1);
        
            for(int i=0;i<n;i++) A[i]=A[i]*A[i];//卷积 
            FFT(A,-1);
            
            for(int i=0;i<n;i++) num[i]=(long long)(A[i].x/n+0.5);//求出num 
            imax=2*branch[N-1];//最大的和为imax  
            for(int i=0;i<N;i++) num[branch[i]*2]--;//把自己和自己的组成的都减去 
            for(int i=1;i<=imax;i++) num[i]/=2;//1 2与2 1我们计算了两次 所有整体除二 
            sum[0]=0;
            for(int i=1;i<=imax;i++) sum[i]=sum[i-1]+num[i];
            
            long long cnt=0;
            for(int i=0;i<N;i++) 
            {
                cnt+=sum[imax]-sum[branch[i]];
                //减掉一个取大,一个取小的
                cnt-=(long long)(N-1-i)*i;
                //减掉一个取本身,另外一个取其它
                cnt-=(N-1);
                //减掉大于它的取两个的组合
                cnt-=(long long)(N-1-i)*(N-i-2)/2;
            }
            long long tot = (long long)N*(N-1)*(N-2)/6;
            printf("%.7lf
    ",(double)cnt/tot);
        }
        return 0;
    }
    View Code

  • 相关阅读:
    【转载】ARM与单片机的区别
    关于头文件定义的一点思考
    关于*** WARNING L15: MULTIPLE CALL TO SEGMENT
    【转】单片机中volatile定义的作用详解
    关于单片机位数的思考(8位、16位、32位)
    memcpy函数
    ubuntu下打开windows里的txt文件乱码解决
    linux source filename
    linux环境设置export
    pdf转word工具
  • 原文地址:https://www.cnblogs.com/Heey/p/9055495.html
Copyright © 2011-2022 走看看