[HDU4609] 3-idiots - 多项式乘法,FFT

Posted mollnn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HDU4609] 3-idiots - 多项式乘法,FFT相关的知识,希望对你有一定的参考价值。

题意:有\(n\)个正整数,求随机选取一个3组合,能构成三角形的概率。

Solution: 很容易想到构造权值序列,对其卷积得到任取两条边(可重复)总长度为某数时的方案数序列,我们希望将它转化为两条边不可重复,并去掉顺序。不妨设给定的 \(N\) 个正整数的集合为 \(S\),卷积后的权值序列为 \(\c_i\\) ,那么我们对每一个 \(x \in S\), 对 \(c_x\) 减去 \(1\) 即可。

不妨设选出的组合为 \((i,j,k)\),假设 \(x_i\) 为已经排序的长度序列,那么我们不妨枚举最长边下标 \(k\),那么这一次的贡献为

\[\sum_k \in S(\sum_i=k+1^\inftyc_i)\]

前缀和扫一遍即可。当然问题并没有这么简单,我们这样获得的答案无法满足下列约束条件

\[k \geq i, k \geq j, i \neq k, j \neq k \]

如果所有数字都不同,那么去重将是非常容易的。我们对排序后序列枚举元素下标 \(i \in [0,n)\),那么很显然以 \(i\) 为最大边时非法的共有三类。不妨设\(a_i<a_j\)

满足\(a_i>a_k, a_j>a_k\)的共有

\[\frac(n-i-1)(n-i-2)2\]

满足\(a_i<a_k, a_j>a_k\)的共有

\[(n-i-1)i\]

满足\(a_i=a_k or a_j=a_k\)的共有

\[n-1\]

于是我们暴力统计一遍就可以得到非法数量,进而得出答案。

原问题是可以出现相同大小的数字的,但容易发现它的去重情况与全部不相同并没有什么差异。因而直接按照上述做法即可得出答案。

我们也可以对这个转化的正确性稍作证明。我们将原始序列中的每一个元素 \(x_i\) 映射到一个新元素

\[y_i = x_i - 0.2 + 0.1 \cdot \fracin\]

容易证明\(x_i,x_j,x_k\)可以构成三角形当且仅当\(y_i,y_j,y_k\)可以构成三角形。因此我们通过构造说明了这个转化的正确性。

笔者菜甚,尝试用前缀和直接求解带重复情况失败,留坑在此。

const int N = 200005;
int T,n,m,x[N],a[N],s[N];
double c[N];
double t[N];

int main() 
    ios::sync_with_stdio(false);
    cin>>T;
    while(T--) 
        cin>>n;
        memset(a,0,sizeof a);
        memset(x,0,sizeof x);
        memset(s,0,sizeof s);
        memset(c,0,sizeof c);
        memset(t,0,sizeof t);
        for(int i=0;i<n;i++) cin>>x[i];
        for(int i=0;i<n;i++) m=max(x[i],m);
        for(int i=0;i<n;i++) a[x[i]]++;
        poly p,q;
        p.c.resize(m+1);
        for(int i=0;i<=m;i++) p.c[i]=a[i];
        q=p;
        double ans = 0;
        p*=q;
        for(int i=0;i<=2*m;i++) c[i]=(p.c[i]);
        for(int i=0;i<n;i++) c[x[i]*2]-=1;
        for(int i=0;i<=2*m;i++) c[i]/=2.0;
        t[0]=c[0];
        for(int i=1;i<=2*m;i++) t[i]=t[i-1]+c[i];
        for(int i=0;i<n;i++) ans+=(t[2*m]-t[x[i]]);
        sort(x,x+n);
        for(int i=0;i<n;i++) 
            ans -= (long long)(n-i-1)*(long long)(n-i-2)/2 + (long long)(n-i-1)*(long long)i + (n-1);
        
        printf("%.7f\n",6.0*(double)ans / ((double)n*(n-1.0)*(n-2.0)));
    

以上是关于[HDU4609] 3-idiots - 多项式乘法,FFT的主要内容,如果未能解决你的问题,请参考以下文章

hdu4609 3-idiots

[HDU4609] 3-idiots - 多项式乘法,FFT

HDU 4609 3-idiots

hdu 4609 3-idiots —— FFT

HDU 4709 3-idiots FFT 多项式

HDU4609 3-idiots(母函数 + FFT)