idiots bzoj-3513 MUTC-2013
题目大意:给定$n$根木棍,问随机选择三根能构成三角形的概率。
注释:$1le nle 3cdot 10^5$,$1le a_ile 10^5$。
想法:
考虑暴力:枚举三条边。复杂度$O(n^3)$。
优化一下发现第三条边可以二分。复杂度$O(n^2logn)$。
正解:
设$g_i$表示选取两根长度为$i$的方案数。
直接用$g$统计不合法情况即可。
发现$g$可以用桶自卷积完成,而这个过程可以用$FFT$优化。
Code:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define N 100010 using namespace std; typedef double db; const db pi=acos(-1); typedef long long ll; inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} int rd() {int x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;} struct cp { db x,y; cp() {x=y=0;} cp(db x_,db y_) {x=x_,y=y_;} cp operator + (const cp &a) const {return cp(x+a.x,y+a.y);} cp operator - (const cp &a) const {return cp(x-a.x,y-a.y);} cp operator * (const cp &a) const {return cp(x*a.x-y*a.y,x*a.y+y*a.x);} }a[N<<2]; int n,maxx,b[N];ll num[100005]; void fft(cp *a,int len,int flg) { int i,j,k,t; cp w,wn,tmp; for(i=k=0;i<len;i++) { if(i>k) swap(a[k],a[i]); for(j=len>>1;(k^=j)<j;j>>=1); } for(k=2;k<=len;k<<=1) { wn=cp(cos(2*pi*flg/k),sin(2*pi*flg/k)); t=k>>1; for(i=0;i<len;i+=k) { w=cp(1,0); for(j=i;j<i+t;j++) { tmp=a[j+t]*w; a[j+t]=a[j]-tmp; a[j]=a[j]+tmp; w=w*wn; } } } if(flg==-1) for(i=0;i<len;i++) a[i].x/=len; } int main() { int T,i,x;scanf("%d",&T);int len; while(T--) { memset(num,0,sizeof num); ll ans=0; maxx=0; memset(a,0,sizeof(a)); n=rd(); for(i=1;i<=n;i++) x=rd(),b[i]=x,a[x].x++,maxx=max(x,maxx); len=1; while(len<(maxx<<1)) len<<=1; fft(a,len,1); for(i=0;i<len;i++) a[i]=a[i]*a[i]; fft(a,len,-1); for(i=1;i<=n;i++) a[b[i]<<1].x--; for(i=1;i<=maxx;i++) num[i]=num[i-1]+(ll)(a[i].x/2+0.1); for(i=1;i<=n;i++) ans+=num[b[i]]; printf("%.7f ",1-(1.0*ans/(1.0*n*(n-1)/6*(n-2)))); } return 0; }
小结:从暴力入手其实并没有那么万能。比如这个题其实非常的考察思维。