HDU4609 3-idiots

多校那时候我还年轻,没能理解FFT,只知道它能够进行信号在时域和频域上的转换。

现在学习了FFT在多项式乘法上的应用,感觉对FFT的理解更深了一层。

要搞懂FFT,首先要理解卷积。


先抄一下wikipedia

函数 f 与 g 的卷积记作f * g,它是其中一个函数翻转并平移后与另一个函数的乘积的积分,是一个对平移量的函数。

(f * g )(t) = \int f(\tau) g(t - \tau)\, d\tau

积分区间取决于 f 的定义域与 g 的定义域。

对于定义在离散域的函数,卷积定义为

(f  * g)[m] = \sum_n {f[n] g[m - n]}


理解了卷积之后,我们可以把FFT先简单的理解为求卷积的O(nlogn)算法。

然后进入正题,本题中我们已知每一种长度的棒子各有多少个,换个思路想想,我们就知道了一个多项式的各项系数,把这个多项式与其自身做快速傅里叶变换,就可以得到新多项式的各项系数,对于新系数ki',有k[i]' = sigma( k[j] + k[i-j] ),而这里的系数正是由两根棒子加起来的长度的种数,假设用cnt[i]表示和的长度为i的棒子组合有多少种。


求出数组cnt[]之后,这个题目已经做了一半了,下面是计数问题。


(1) 首先去掉cnt里重复的部分,一根棒子不能与自己组合,所以有cnt[a[i] * 2] --

(2) 任意两个棒子组合的顺序我们不需要考虑,而实际上他们算了两次,所以有cnt[i] /=2

(3) 枚举所有棒子,假设这根棒子a[i]是组成三角形其中最长的那根,我们首先求出另外两根的长度和比它的长的总种数,即sigma(cnt[j])   (a[i]<j<=crest*2,crest是最长的棒子长度)

(4) 另外两根中,这根被枚举的棒子不能再出现了,所以要减去n-1

(5)  另外两根中,可能有一根长度大于a[i],另一根小于a[i],所以要减去(n-i) * (i-1)

(6) 另外两根中,可能两根长度都大于a[i],再减去(n-i) * (n-i-1) / 2


计算下所有可能的种数 tot = n * (n-1) * (n-2) / 6

把统计出的答案除以tot就行了。


注意:cnt 和 tot 都有可能超过int的范围,所以应该用long long 



贴代码:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <ctime>
#include <climits>
#include <cmath>
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <list>
#include <queue>
#include <stack>
#include <deque>
#include <algorithm>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int maxn = 100010;

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

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

void fft(Complex *y, int len, int on)
{
    brc(y, len);
    for (int i = 2; i <= len; i <<= 1)
    {
        Complex wn(cos(on*2.0*PI/i), sin(on*2.0*PI/i));
        for (int j = 0; j < len; j += i)
        {
            Complex w(1.0, 0.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;
}

int T,n;
int a[maxn];
ll cnt[maxn*4], sum[maxn*4], ans, tot;
Complex x1[maxn*4];

int main()
{
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        memset(cnt, 0, sizeof(cnt));
        int len = 1, crest = 0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            if (a[i] > crest) crest = a[i];
            cnt[a[i]]++;
        }
        crest++;

        while (len < crest*2) len <<= 1;
        for (int i = 0; i < len; i++) x1[i] = Complex(cnt[i], 0.0);
        fft(x1, len, 1);
        for (int i = 0; i < len; i++) x1[i] = x1[i] * x1[i];
        fft(x1, len, -1);
        for (int i = 0; i < len; i++) cnt[i] = (ll)(x1[i].r + 0.5);
        len = crest * 2;

        for (int i = 1; i <= n; i++) cnt[a[i]*2]--;
        for (int i = 1; i <= len; i++) cnt[i] /= 2;
        sum[0] = 0;
        for (int i = 1; i <= len; i++) sum[i] = sum[i-1] + cnt[i];
        ans = 0;
        for (int i = 1; i <= n; i++)
        {
            ans += sum[len] - sum[a[i]];
            ans -= n - 1;
            ans -= (ll)(i - 1) * (n - i);
            ans -= (ll)(n - i) * (n - i - 1) / 2LL;
        }
        tot = (ll)n * (n - 1) * (n - 2) / 6LL;
        printf("%.7lf\n", (double)ans / tot);
    }
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值