【BZOJ3515】[MUTC2013]idiots
Description
给定n个长度分别为a_i的木棒,问随机选择3个木棒能够拼成三角形的概率。
Input
第一行T(T<=100),表示数据组数。
接下来若干行描述T组数据,每组数据第一行是n,接下来一行有n个数表示a_i。
3≤N≤10^5,1≤a_i≤10^5
Output
T行,每行一个整数,四舍五入保留7位小数。
Sample Input
2
4
1 3 3 4
4
2 3 3 4
4
1 3 3 4
4
2 3 3 4
Sample Output
0.5000000
1.0000000
1.0000000
HINT
T<=20
N<=100000
题解:我们依旧采用补集法,如果我们求出任意s[i]表示选两个木棍使得长度和为i的方案数,那么可以枚举最长边,统计∑s[a[i]]即可。
那么如何求出s数组呢?FFT。
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #define pi acos(-1.0) using namespace std; typedef long long ll; struct cp { double x,y; cp () {} cp (double a,double b){x=a,y=b;} cp operator + (cp a) {return cp(x+a.x,y+a.y);} cp operator - (cp a) {return cp(x-a.x,y-a.y);} cp operator * (cp a) {return cp(x*a.x-y*a.y,x*a.y+y*a.x);} }l1[1<<20],l2[1<<20]; int n,m,len; int v[1<<20]; ll ans,sum; ll s[1<<20]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } void FFT(cp *a,int f) { int i,j,k,h; cp t; for(i=k=0;i<len;i++) { if(i>k) swap(a[i],a[k]); for(j=(len>>1);(k^=j)<j;j>>=1); } for(h=2;h<=len;h<<=1) { cp wn(cos(2*pi*f/h),sin(2*pi*f/h)); for(j=0;j<len;j+=h) { cp w(1,0); for(k=j;k<j+h/2;k++) t=w*a[k+h/2],a[k+h/2]=a[k]-t,a[k]=a[k]+t,w=w*wn; } } } void calc(cp *a) { FFT(a,1); for(int i=0;i<len;i++) a[i]=a[i]*a[i]; FFT(a,-1); for(int i=0;i<len;i++) s[i]+=ll(a[i].x/len+0.1); } void work() { n=rd(),m=0; int i; for(i=1;i<=n;i++) v[i]=rd(),m=max(m,v[i]<<1); for(len=1;len<=m+1;len<<=1); for(i=0;i<len;i++) l1[i]=cp(0,0); memset(s,0,sizeof(s)); for(i=1;i<=n;i++) l1[v[i]].x+=1,s[v[i]<<1]--; calc(l1); for(i=1;i<=m;i++) s[i]+=s[i-1]; ans=(ll)n*(n-1)*(n-2)/6,sum=0; for(i=1;i<=n;i++) sum+=s[v[i]]; printf("%.7lf ",(1-0.5*sum/ans)); } int main() { int T=rd(); while(T--) work(); return 0; }//2 4 1 3 3 4 4 2 3 3 4