1. 程式人生 > >解題:HDU 4609 Three Idiots

解題:HDU 4609 Three Idiots

題面

要求組合的方法顯然我們需要對桶卷積,即設$F(x)=\sum\limits_{i=1}^{maxx}x^{cnt[i]}$,然後我們初步的先把$F^2(x)$卷出來,表示選兩條邊。然後我們發現如果用“兩邊之和大於第三邊”來求,那麼小於這兩條邊的可能不是最長的,所以應該列舉大於這兩條邊的來容斥

注意題目中提到了不能選重複的,所以對於所有指數為偶數的項去重,還有題目要求是無序地選

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5
using namespace std; 6 const int N=400005,M=30,K=4e5; 7 const double pai=acos(-1); 8 struct cpx 9 { 10 double x,y; 11 }a[N]; 12 cpx operator + (cpx a,cpx b) 13 { 14 return (cpx){a.x+b.x,a.y+b.y}; 15 } 16 cpx operator - (cpx a,cpx b) 17 { 18 return (cpx){a.x-b.x,a.y-b.y}; 19 } 20 cpx operator
* (cpx a,cpx b) 21 { 22 double x1=a.x,x2=b.x,y1=a.y,y2=b.y; 23 return (cpx){x1*x2-y1*y2,x1*y2+x2*y1}; 24 } 25 long long cnt[N],tot,ans; 26 int mem[N],rev[N],lgg[N]; 27 double Sin[M],Cos[M]; 28 int n,m,T,rd; 29 int Round(double x) 30 { 31 return (int)(x+0.5); 32 } 33 void Prework() 34 { 35
scanf("%d",&T); 36 for(int i=2;i<=K;i++) 37 lgg[i]=lgg[i>>1]+1; 38 for(int i=1;i<=25;i++) 39 Sin[i]=sin(2*pai/(1<<i)),Cos[i]=cos(2*pai/(1<<i)); 40 } 41 void Trans(cpx *c,int t) 42 { 43 for(int i=0;i<n;i++) 44 if(rev[i]>i) swap(c[rev[i]],c[i]); 45 for(int i=2;i<=n;i<<=1) 46 { 47 int len=i>>1; 48 cpx omg=(cpx){Cos[lgg[i]],Sin[lgg[i]]*t}; 49 for(int j=0;j<n;j+=i) 50 { 51 cpx ori=(cpx){1,0},tmp; 52 for(int k=j;k<j+len;k++,ori=ori*omg) 53 tmp=ori*c[k+len],c[k+len]=c[k]-tmp,c[k]=c[k]+tmp; 54 } 55 } 56 if(t==-1) for(int i=0;i<n;i++) c[i].x/=n; 57 } 58 int main() 59 { 60 Prework(); 61 while(T--) 62 { 63 scanf("%d",&n); 64 memset(mem,0,sizeof mem),m=0; 65 for(int i=1;i<=n;i++) 66 { 67 scanf("%d",&rd); 68 mem[rd]++,m=max(m,rd); 69 } 70 ans=tot=1ll*n*(n-1)*(n-2)/6,n=1; while(n<=m*2) n<<=1; 71 for(int i=0;i<n;i++) a[i].x=mem[i],a[i].y=0; 72 for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)+(i&1)*(n>>1); 73 Trans(a,1); 74 for(int i=0;i<n;i++) a[i]=a[i]*a[i]; 75 Trans(a,-1); 76 for(int i=1;i<=m;i++) cnt[i]=Round(a[i].x); 77 for(int i=1;i<=m;i++) 78 { 79 if(i%2==0) cnt[i]-=mem[i>>1]; 80 cnt[i]>>=1,cnt[i]+=cnt[i-1]; 81 } 82 for(int i=1;i<=m;i++) ans-=cnt[i]*mem[i]; 83 printf("%.7f\n",(double)ans/tot); 84 } 85 return 0; 86 }
View Code