【FFT】HDU4609-3 idiots

..退化为一天两题了,药丸..

【题目大意】

给出n根木棍的长度,求从其中取出3根能组成三角形的概率。

【思路】

然后枚举求前缀和,枚举最长边。假设最长边为l,先求出所有两边之和大于它的情况数。然后减去两边都大于它的情况以及一大一小的情况。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<complex>
  6 #include<cmath>
  7 #define pi acos(-1)
  8 using namespace std;
  9 const int MAXN = 400040;
 10 typedef complex<double> com;
 11 typedef long long ll;
 12 int n,m,L,tmpn;
 13 com a[MAXN],b[MAXN];
 14 int c[MAXN],Rev[MAXN],l[MAXN],len; 
 15 ll sum[MAXN],num[MAXN];//把sum和num放在子程序里就会错误,放进主程序就可以,为什么?? 
 16 
 17 void get_bit(){for (n=1,L=0;n<m;n<<=1) L++;} 
 18 void get_Rtable(){for (int i=0;i<n;i++) Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(L-1));}
 19 void multi(com* a,com* b){for (int i=0;i<n;i++) a[i]*=b[i];}
 20 
 21 void FFT(com* a,int flag)
 22 {
 23     for (int i=0;i<n;i++)if(i<Rev[i])swap(a[i],a[Rev[i]]); //利用逆序表,快速求逆序
 24     for (int i=1;i<n;i<<=1)
 25     {
 26         com wn(cos(2*pi/(i*2)),flag*sin(2*pi/(i*2)));
 27         for (int j=0;j<n;j+=(i<<1))
 28         {
 29             com w(1,0);
 30             for (int k=0;k<i;k++,w*=wn)
 31             {
 32                 com x=a[j+k],y=w*a[j+k+i];
 33                 a[j+k]=x+y;
 34                 a[j+k+i]=x-y;
 35             }
 36         }
 37     }
 38     if (flag==-1) for (int i=0;i<n;i++) a[i]/=n;
 39 }
 40 
 41 void init()
 42 {
 43     int tmp[MAXN/4];
 44     scanf("%d",&n);
 45     tmpn=n;
 46     memset(tmp,0,sizeof(tmp));
 47     memset(Rev,0,sizeof(Rev));
 48     len=-1;
 49     for (int i=0;i<n;i++)
 50     {
 51         scanf("%d",&l[i]);
 52         if (len<l[i]) len=l[i];
 53         tmp[l[i]]++;
 54     } 
 55     for (int i=0;i<MAXN;i++) a[i]=b[i]=(0);
 56     for (int i=1;i<=len;i++) a[i]=(tmp[i]);
 57 }
 58 
 59 void solve()
 60 {
 61     m=len<<1;
 62     len++;m++;
 63     get_bit();
 64     get_Rtable();
 65     FFT(a,1);
 66     for (int i=0;i<n;i++) b[i]=a[i];
 67     multi(a,b);
 68     FFT(a,-1);
 69 }
 70 
 71 void get_ans()
 72 {    
 73     memset(sum,0,sizeof(sum));
 74     memset(num,0,sizeof(num));
 75     for (int i=1;i<m;i++) num[i]=(ll)(a[i].real()+0.5);
 76     //减掉取两个相同的组合
 77     for(int i =0;i<tmpn;i++)
 78     {
 79         num[l[i]+l[i]]--;
 80     }    
 81     for (int i=1;i<m;i++) num[i]/=2;
 82     sum[0]=0;
 83     
 84     for (int i=1;i<m;i++) sum[i]=sum[i-1]+num[i];
 85     
 86     ll cnt=0;
 87     n=tmpn;
 88     for (int i=0;i<n;i++)
 89     {
 90         cnt+=sum[m-1]-sum[l[i]];
 91         //减掉一个取大,一个取小的
 92         cnt-= (ll)(n-1-i)*i;
 93         //减掉一个取本身,另外一个取其它
 94         cnt-= (n-1);
 95         //减掉大于它的取两个的组合
 96         cnt-= (ll)(n-1-i)*(n-i-2)/2;
 97     }
 98     ll tot = (ll)n*(n-1)*(n-2)/6;
 99     printf("%.7lf\n",(double)cnt/tot);
100 
101 }
102 
103 int main()
104 {
105     int T;
106     scanf("%d",&T);
107     while (T--)
108     {
109         init();
110            solve();
111         get_ans();
112     }
113     return 0;
114 }

 

posted @ 2016-07-20 17:57  iiyiyi  阅读(157)  评论(0编辑  收藏  举报