您的位置:首页 > 其它

HDU 4609 FFT

2015-10-26 12:28 211 查看
题目大意

给定n条边的边值,求任意取三条边能组成三角形的概率

这里概率 P = valid/tot

tot = (n-2)*(n-1)*n/6是没问题的

valid表示合法的方式

先考虑,任意两条边组合形成方法的总数

因为边值在100000的范围内,这里组合用fft计算

得到最后形成和为 i 的两条边的方法数为 num[i]

这里计算后要记得减去取两条相同边的情况,还有 取 3,4 和 4,3是一样的,要记得除以2

最后跑个n的循环,每次将当前边作为排序后(主要是因为有长度相同的边才这么考虑)次序最大的边,然后保证得到的和是两条在它前面的边组成的

这个计算就需要求个总方法数的前缀和了

取到所有和大于当前边的方法数,减去由两条比它大的边组成的情况(n-i)*(n-i-1)/2,一个比他大一个比他小组成的(n-i)*(i-1),还有它其余任意一条边组成的(n-1)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 100005
#define ll long long
const double PI = acos(-1.0);

int n , a
, cnt
;
ll num[N<<2] , sum[N<<2];

struct complex{
double r , i;
complex(double r=0 , double i=0):r(r),i(i){}
complex operator+(const complex &a) const{
return complex(r+a.r , i+a.i);
}
complex operator-(const complex &a) const{
return complex(r-a.r , i-a.i);
}
complex operator*(const complex &a) const{
return complex(r*a.r-i*a.i , r*a.i+i*a.r);
}
};

void change(complex y[] , int len)
{
int i,j,k;
for(i=1 , j=len/2 ; i<len-1 ; i++){
if(i<j) swap(y[i],y[j]);
k = len/2;
while(j>=k){
j-=k;
k/=2;
}
if(j<k) j+=k;
}
}

void fft(complex y[] , int len , int on)
{
change(y , len);
for(int i=2 ; i<=len ; i<<=1){
complex wn(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 = y[k];
complex t = w*y[k+i/2];
y[k] = u+t;
y[k+i/2] = u-t;
w = w*wn;
}
}
}
if(on==-1)
for(int i=0 ; i<len ; i++)
y[i].r /= len;

}
complex x[N<<2];

int main()
{
// freopen("a.in" , "r" , stdin);
int T;
scanf("%d" , &T);
while(T--){
scanf("%d" , &n);
int mx = 0;
memset(cnt , 0 , sizeof(cnt));
for(int i=1 ; i<=n ; i++){
scanf("%d" , &a[i]);
cnt[a[i]]++ , mx=max(mx , a[i]);
}
int len = 1;
while(len<2*(mx+1)) len<<=1;
for(int i=0 ; i<=mx ; i++) x[i] = complex(cnt[i] , 0);
for(int i=mx+1 ; i<len ; i++) x[i] = complex(0 , 0);
fft(x , len , 1);
for(int i=0 ; i<len ; i++)
x[i] = x[i]*x[i];
fft(x , len , -1);
for(int i=0 ; i<len ; i++) num[i] = (ll)(x[i].r+0.5);
for(int i=1 ; i<=n ; i++) num[a[i]+a[i]]--;
for(int i=0 ; i<len ; i++){
num[i]/=2;
if(i) sum[i] = sum[i-1]+num[i];
}
ll ans = 0;
sort(a+1 , a+n+1);
for(int i=1 ; i<=n ; i++){
ll val = sum[len-1]-sum[a[i]];
val = val - (n-1);
val = val - (ll)(n-i)*(i-1);
val = val - (ll)(n-i)*(n-i-1)/2;
//  cout<<i<<" "<<a[i]<<" "<<sum[len-1]-sum[a[i]]<<" "<<val<<endl;
ans = ans+val;
}
printf("%.7f\n" , ans*6.0/((ll)n*(n-1)*(n-2)));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: