题目大意:
给定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 ",(double)cnt/tot); } return 0; }