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