您的位置:首页 > 大数据 > 物联网

hdu 4609 3-idiots (FFT)

2014-08-06 23:13 435 查看
转自http://www.cnblogs.com/kuangbin/archive/2013/07/24/3210565.html

学会了FFT。

这题就很容易了。

其实题目是给了n条线段。问随机取三个,可以组成三角形的概率。



其实就是要求n条线段,选3条组成三角形的选法有多少种。



首先题目给了a数组,

如样例一:

4

1 3 3 4

把这个数组转化成num数组,num[i]表示长度为i的有num[i]条。

样例一就是

num = {0 1 0 2 1}

代表长度0的有0根,长度为1的有1根,长度为2的有0根,长度为3的有两根,长度为4的有1根。

使用FFT解决的问题就是num数组和num数组卷积。

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 }

长度为n的数组和长度为m的数组卷积,结果是长度为n+m-1的数组。



{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



利用FFT可以快速求取循环卷积,具体求解过程不解释了,就是DFT和FFT的基本理论了。

总之FFT就是快速求到了num和num卷积的结果。只要长度满足>=n+m+1.那么就可以用循环卷积得到线性卷积了。



弄完FFT得到一个num数组,这个数组的含义在上面解释过了。

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <functional>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <cstdlib>
#include <ctime>
//#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
#define INF 1e9
const int maxn = 400005;
//#define mod 1000000007
#define eps 1e-7
#define pi 3.1415926535897932384626433
#define rep(i,n) for(int i=0;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define scan(n) scanf("%d",&n)
#define scanll(n) scanf("%I64d",&n)
#define scan2(n,m) scanf("%d%d",&n,&m)
#define scans(s) scanf("%s",s);
#define ini(a) memset(a,0,sizeof(a))
#define out(n) printf("%d\n",n)
//ll gcd(ll a,ll b) { return b==0?a:gcd(b,a%b);}
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const double PI = acos(-1.0);
//复数结构体
struct complex
{
    double r,i;
    complex(double _r = 0.0,double _i = 0.0)
    {
        r = _r; i = _i;
    }
    complex operator +(const complex &b)
    {
        return complex(r+b.r,i+b.i);
    }
    complex operator -(const complex &b)
    {
        return complex(r-b.r,i-b.i);
    }
    complex operator *(const complex &b)
    {
        return complex(r*b.r-i*b.i,r*b.i+i*b.r);
    }
};
/*
 * 进行FFT和IFFT前的反转变换。
 * 位置i和 (i二进制反转后位置)互换
 * len必须去2的幂
 */
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]);
        //交换互为小标反转的元素,i<j保证交换一次
        //i做正常的+1,j左反转类型的+1,始终保持i和j是反转的
        k = len/2;
        while( j >= k)
        {
            j -= k;
            k /= 2;
        }
        if(j < k) j += k;
    }
}
/*
 * 做FFT
 * len必须为2^k形式,
 * on==1时是DFT,on==-1时是IDFT
 */
void FFT(complex y[],int len,int on)
{
    change(y,len);
    for(int h = 2; h <= len; h <<= 1)
    {
        complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
        for(int j = 0;j < len;j+=h)
        {
            complex w(1,0);
            for(int k = j;k < j+h/2;k++)
            {
                complex u = y[k];
                complex t = w*y[k+h/2];
                y[k] = u+t;
                y[k+h/2] = u-t;
                w = w*wn;
            }
        }
    }
    if(on == -1)
        for(int i = 0;i < len;i++)
            y[i].r /= len;
}
int n;
int a[maxn];
complex A[maxn];
ll sum[maxn];
ll num[maxn];
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	//     freopen("out.txt","w",stdout);
#endif  
	int T;
	cin>>T;
	while(T--)
	{
		scanf("%d",&n);
		ini(num),ini(sum),ini(A);
		rep(i,n)
		{
			scan(a[i]);
			num[a[i]] ++;
		}
		sort(a,a+n);
		int len = 1;
		while(len < a[n-1] * 2 + 2) len <<= 1;
		rep(i,len) A[i] = complex(num[i],0);
		FFT(A,len,1);
		rep(i,len) A[i] = A[i] * A[i];
		FFT(A,len,-1);
		rep(i,len) num[i] = (ll)(A[i].r + 0.5);
	//	len = 2 * a[n-1];
		rep(i,n)
		{
			num[a[i] + a[i]] --; 
		}
		rep1(i,len)
		{
			num[i] /= 2;
		}
		sum[0] = 0;
		rep1(i,len)
		{
			sum[i] = sum[i-1] + num[i];
		}

		ll cnt = 0;
		for(int i = 0;i < n; i++) //枚举最长边
		{
			cnt += sum[len] - sum[a[i]];
			cnt -= 1LL * (n - i - 1) * i;
			cnt -= n - 1;
			cnt -= 1LL * (n-i-1) * (n-i-2) / 2;
		}

		ll tol = 1LL* n * (n-1) * (n-2) / 6;
		printf("%.7lf\n",cnt * 1.0 / tol);
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: