3513: [MUTC2013]idiots
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 348 Solved: 111
[Submit][Status][Discuss]
Description
给定n个长度分别为a_i的木棒,问随机选择3个木棒能够拼成三角形的概率。
Input
第一行T(T<=100),表示数据组数。
接下来若干行描述T组数据,每组数据第一行是n,接下来一行有n个数表示a_i。
Output
T行,每行一个整数,四舍五入保留7位小数。
Sample Input
2
4
1 3 3 4
4
2 3 3 4
Sample Output
0.5000000
1.0000000
HINT
T<=20
N<=100000
要求能组成三角形的概率,可以转化成1-不能组成三角形的概率。
这个题没有告诉
ai
的范围,这里
ai
的范围跟N是差不多的,因为这样这个题才能这样做。
用
sum[i]
表示i这个数出现的次数。因为三角形的两条段的边的和要大于第三条边,所以我们只需要统计出比i短的边有多少种组合能组成长度<=i就好了。
也就是要求
sum[i]=∑i−1j=1sum[j]∗sum[i−1−j]
这个式子是一个卷积的形式,用FFT优化一下就好了。
复杂度
O(TNlongN)
需要注意的问题:
①:多组数据的时候,rev和dig都要清零。
②:在做完FFT之后,一定要认真想想怎么统计答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define LL long long
const int M=500000;
int T,n,sum[M],rev[M],N,L,dig[M];
struct S{
double x,y;
S operator + (const S&xx){return (S){x+xx.x,y+xx.y};}
S operator - (const S&xx){return (S){x-xx.x,y-xx.y};}
S operator * (const S&xx){return (S){x*xx.x-y*xx.y,x*xx.y+y*xx.x};}
}a[M],b[M],c[M],d[M];
inline int in(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void FFT(S *a,int f){
int i,j,k;
S wn,w,x,y;
for(i=0;i<N;++i) d[i]=a[rev[i]];
for(i=0;i<N;++i) a[i]=d[i];
for(i=2;i<=N;i<<=1){
wn=(S){cos(2*M_PI/i),f*sin(2*M_PI/i)};
for(j=0;j<N;j+=i){
w=(S){1.,0.};
for(k=j;k<j+i/2;++k){
x=a[k];
y=a[k+i/2]*w;
a[k]=x+y;
a[k+i/2]=x-y;
w=w*wn;
}
}
}
if(f==-1) for(i=0;i<N;++i) a[i].x/=(double)N*1.0;
}
int main(){
int i,x,o,j,len,maxn;
T=in();
for(o=1;o<=T;++o){
n=in();
memset(sum,0,sizeof(sum));
for(maxn=0,i=1;i<=n;++i){
x=in();
++sum[x];
maxn=max(maxn,x);
}
maxn+=1;
for(L=0,N=1;N<maxn;N<<=1,L+=1); N<<=1;L+=1;
memset(dig,0,sizeof(dig));
memset(rev,0,sizeof(rev));
for(i=0;i<N;++i){
a[i].x=a[i].y=b[i].x=b[i].y=0;
for(j=i,len=0;j;j>>=1) dig[len++]=j&1;
for(j=0;j<L;++j) rev[i]=rev[i]*2+dig[j];
}
for(i=1;i<=maxn;++i){
a[i]=(S){(double)sum[i],0.};
b[i]=(S){(double)sum[i],0.};
}
FFT(a,1);FFT(b,1);
for(i=0;i<N;++i) c[i]=a[i]*b[i];
FFT(c,-1);
LL ans=0,tot=(LL)n*(LL)(n-1)*(LL)(n-2)/6LL,peace=0;
for(ans=0,i=0;i<=maxn;++i){
peace+=(LL)(c[i].x+0.5);
if(!(i%2)) peace-=sum[i/2];
if(!sum[i]) continue;
ans+=peace*sum[i];
}
ans/=2;
printf("%.7f\n",(double)1.-(double)((double)ans*1./(double)tot*1.));
}
}