1. 程式人生 > >HDU#4609. 3-idiots

HDU#4609. 3-idiots

col int 給定 clu 不同 CA 前綴 turn 組成


題目大意:

給定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\n",(double)cnt/tot); } return 0; }
View Code


HDU#4609. 3-idiots