现在的位置: 首页 > 综合 > 正文

hdu 4609 3-idiots

2017年12月16日 ⁄ 综合 ⁄ 共 1849字 ⁄ 字号 评论关闭

FFT乘法

fft在超大整数乘法中的应用

貌似能把时间从o(n^2) 降低到 o(n logn)

#include <stdio.h>   
#include <string.h>
#include <math.h>
#include <algorithm>
#ifdef __GNUC__
#endif // __GNUC__
using namespace std;
typedef long long ll;
const int MAXN = 400100;
const double PI = acos(-1.0);
struct mcomplex
{
	double r, i;
	mcomplex(double rr=0.0, double ii = 0.0):r(rr),i(ii){}
	mcomplex operator + (mcomplex &a)
	{
		return mcomplex(r+a.r, i+a.i);
	}
	mcomplex operator - (mcomplex &a)
	{
		return mcomplex(r-a.r, i-a.i);
	}
	mcomplex operator * (mcomplex &a)
	{
		return mcomplex(r*a.r-i*a.i, r*a.i+i*a.r);
	}
};
int a[MAXN>>2];
mcomplex xl[MAXN];
ll num[MAXN], sum[MAXN];
void change(mcomplex 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(mcomplex y[], int len, int on)
{
	change(y, len);
	for (int h =2; h <= len; h<<=1)
	{
		mcomplex wn(cos(-on*2*PI/h), sin(-on*2*PI/h));
		for (int j = 0; j< len; j+=h)
		{
			mcomplex w(1, 0);
			for (int k = j; k< j+h/2; ++k)
			{
				mcomplex u = y[k];
				mcomplex 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 main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
	int t, n, i;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &n);
		memset(num, 0, sizeof num);
		for (i = 0; i< n; ++i)
		{
			scanf("%d", &a[i]);
			num[a[i]]++;
		}
		sort(a, a+n);
		int crest = a[n-1] + 1;
		int len = 1;
		while ( len < 2*crest ) len <<= 1;
		for ( i = 0; i< crest; ++i)			xl[i] = mcomplex(num[i], 0.0);
		for (i = crest ; i< len; ++i)			xl[i] = mcomplex(0, 0);
		fft(xl, len, 1);
		for (i = 0; i< len; ++i)			xl[i] = xl[i]*xl[i];
		fft(xl, len, -1);
		for (i = 0; i< len; ++i)			num[i] = (ll)(xl[i].r + 0.5);
		///.....................................
		len = 2*a[n-1];
		/// 与自身组合
		for (i = 0; i< n; ++i)			--num[a[i]<<1];
		/// 顺序
		for (i = 1; i<= len; ++i)			num[i] >>= 1;
		sum[0] = 0;
		for (i = 1; i<= len; ++i)			sum[i] = sum[i-1] + num[i];
		ll cnt = 0;
		
		for (i = 0; i< n; ++i)
		{
			cnt += sum[len] - sum[a[i]];/// 假设a[i]是最长的棒子,枚举长度大于a[i]的组合数量 
			cnt -= (ll)(n-1-i)*i; /// 一根大于,一根小于
			cnt -= n-1;	/// 另外两根中不能包括自身
			cnt -= (ll)(n-1-i)*(n-i-2)/2;	/// 两根都大于
		}
		ll tot = (ll) n*(n-1)*(n-2)/6;
		printf("%.7lf\n", (double)cnt/tot);
	}
    return 0;
}

抱歉!评论已关闭.