标签:
2 4 1 3 3 4 4 2 3 3 4
0.5000000 1.0000000
题意 :给出n条边,问选出三条边能组成三角形的概率。
第一次搞FFT,了解了下卷积,具体实现是借鉴了别人的代码。
用num[i]表示长度为i的出现几次。对于样例1 3 3 4,我们得到num={0,1,0,2,1},
num数组和num数组卷积的含义:就是从{1 3 3 4}取一个数,从{1 3 3 4}再取一个
数,他们的和每个值各有多少个?
即{0 1 0 2 1}*{0 1 0 2 1} 卷积的结果应该是{0 0 1 0 4 2 4 4 1 }。
这个结果的意义如下:
从{1 3 3 4}取一个数,从{1 3 3 4}再取一个数
取两个数和为 2 的取法是一种:1+1
和为 4 的取法有四种:1+3, 1+3 ,3+1 ,3+1
和为 5 的取法有两种:1+4 ,4+1;
和为 6的取法有四种:3+3,3+3,3+3,3+3,3+3
和为 7 的取法有四种: 3+4,3+4,4+3,4+3
和为 8 的取法有 一种:4+4
<span style="font-size:14px;">#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cmath> #define ll long long using namespace std; const double pi=acos(-1.0); const int maxn=100010; struct Complex { double a,b; Complex(){} Complex(double _a,double _b):a(_a),b(_b){} Complex operator + (const Complex &p) { return Complex(a+p.a,b+p.b); } Complex operator - (const Complex &p) { return Complex(a-p.a,b-p.b); } Complex operator * (const Complex &p) { return Complex(a*p.a-b*p.b,a*p.b+b*p.a); } }s[maxn*4]; int n,len,cnt,a[maxn*4]; ll num[maxn*4],sum[maxn*4]; void initial() { len=1; memset(num,0,sizeof(num)); memset(sum,0,sizeof(sum)); } void input() { int co; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d",&a[i]); num[a[i]]++; } } void ready() { sort(a,a+n); int Max=a[n-1]+1; while(len<2*Max) len<<=1; for(int i=0;i<Max;i++) s[i]=Complex(num[i],0); for(int i=Max;i<len;i++) s[i]=Complex(0,0); } void change() { for(int i=1,j=len/2;i<len-1;i++) { if(i<j) swap(s[i],s[j]); int k=len/2; while(j>=k) { j-=k; k/=2; } if(j<k) j+=k; } } void FFT(int on) { change(); for(int i=2;i<=len;i<<=1) { Complex wn=Complex(cos(-on*2*pi/i),sin(-on*2*pi/i)); for(int j=0;j<len;j+=i) { Complex w(1,0); for(int k=j;k<j+i/2;k++) { Complex u=s[k]; Complex t=w*s[k+i/2]; s[k]=u+t; s[k+i/2]=u-t; w=w*wn; } } } if(on==-1) { for(int i=0;i<len;i++) s[i].a/=len; } } void deal() { FFT(1); for(int i=0;i<len;i++) s[i]=s[i]*s[i]; FFT(-1); cnt=2*a[n-1]; for(int i=0;i<=cnt;i++) num[i]=(ll)(s[i].a+0.5); } void solve() { ll ans=0; for(int i=0;i<n;i++) num[a[i]+a[i]]--; // 减去两次取相同的 for(int i=1;i<=cnt;i++) num[i]/=2; // 对于两次去不同的算了两边,去重 for(int i=1;i<=cnt;i++) sum[i]=sum[i-1]+num[i]; for(int i=0;i<n;i++) { ll t=sum[cnt]-sum[a[i]]; ll b=n-1; // 减去与a[i]组合的 ll c=(ll)i*(n-i-1); // 减去一大一小组合的 ll d=(ll)(n-i-1)*(n-i-2)/2; // 减去两大组合的 ans+=t-b-c-d; } ll mul=(ll)(n)*(n-1)*(n-2)/6; printf("%.7lf\n",ans*1.0/mul); } int main() { int T; scanf("%d",&T); while(T--) { initial(); input(); ready(); deal(); solve(); } return 0; }</span>
标签:
原文地址:http://blog.csdn.net/u012596172/article/details/42918209