冷静 清醒 |

艾特玖

园龄:3年11个月粉丝:12关注:7

Luogu P3338 [ZJOI2014]力

P3338 [ZJOI2014]力

题目大意

给出 n 个数 q1,q2,qn,定义

Fj = i=1j1qi×qj(ij)2  i=j+1nqi×qj(ij)2

Ei = Fiqi

1in,求 Ei 的值。

分析

我们来复现一下推导过程,熟悉一下。

Ej=i<jqi(ij)2i>jqi(ij)2

我们考虑转化,设f[i]=qi,g[i]=1i2,所以原式变为

Ej=i<jf[i]g[ji]i>jf[i]g[ij]

f[0]=0,g[0]=0,考虑一下为什么这么定义?因为我们想让i=j的项无意义,这点对很多推导中的式子定义都有意义。

则我们可以改写式子

Ej=i=0jf[i]g[ji]i=jnf[i]g[ij]

此时可以发现左边已经变为卷积形式了,考虑将右边转化为卷积形式,我们将右边展开

f[j]g[0]+f[j+1]g[1]+f[j+2]g[2]+...+f[j+(nj)]g[nj]

因此右边可以改写为

i=0njf[j+i]g[i]

接下来,我们用到一个技巧,翻转。我们引入f'[i]=f[n-i],由此式子可以写为。

i=0njf[n(j+i)]g[i]

i=0njf[(nj)i)]g[i]

t=n-j,则式子的最终形式为

i=0tf[ti]g[i]

最后总结一下,式子即为

Ej=i=0jf[i]g[ji]+i=0tf[ti]g[i]

最后,我们分别求左边与右边的卷积,最后答案即为ans[j]=LjRnj

Ac_code

#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
const int N = 3e5 + 10;
const double PI = acos(-1);
struct Complex
{
    double x,y;
    Complex(double x = 0,double y = 0) : x(x), y(y) {}
};

//复数乘法:模长相乘,幅度相加
Complex operator * (Complex J, Complex Q) {return Complex(J.x * Q.x - J.y * Q.y, J.x * Q.y + J.y * Q.x);}
Complex operator - (Complex J, Complex Q) {return Complex(J.x - Q.x, J.y - Q.y);}
Complex operator + (Complex J, Complex Q) {return Complex(J.x + Q.x, J.y + Q.y);}

namespace FFT
{
    typedef vector<Complex> poly;
    int R[N];//二进制翻转 二进制位数 补齐的2的整数幂N
    int FFT_init(int n)
    {
        int L = 0,limit = 1;
        while(limit<=n) limit <<= 1,L ++ ;
        // 补成2的整次幂,也就是N
        for(int i = 0; i < limit; ++ i)
            R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));
        return limit;
    }
    void FFT(poly &A, int type,int limit)
    {
        A.resize(limit);
        for(int i = 0; i < limit; ++ i)
            if(i < R[i])
                swap(A[i], A[R[i]]);
            //i小于R[i]时才交换,防止同一个元素交换两次,回到它原来的位置。
        //从底层往上合并
        for(int mid = 1; mid < limit; mid <<= 1) {
            //待合并区间长度的一半,最开始是两个长度为1的序列合并,mid = 1;
            Complex wn(cos(PI / mid), type * sin(PI / mid));//单位根w_n^1;

            for(int len = mid << 1, pos = 0; pos < limit; pos += len) {
                //len是区间的长度,pos是当前的位置,也就是合并到了哪一位
                Complex w(1, 0);//幂,一直乘,得到平方,三次方...

                for(int k = 0; k < mid; ++ k, w = w * wn) {
                    //只扫左半部分,蝴蝶变换得到右半部分的答案,w 为 w_n^k
                    Complex x = A[pos + k];//左半部分
                    Complex y = w * A[pos + mid + k];//右半部分
                    A[pos + k] = x + y;//左边加
                    A[pos + mid + k] = x - y;//右边减
                }
            }
        }
        if(type == 1) return ;
        for(int i = 0; i < limit; ++ i)
            A[i].x /= limit;
            //最后要除以limit也就是补成了2的整数幂的那个N,将点值转换为系数
            //(前面推过了点值与系数之间相除是N)
    }
    poly poly_mul(poly A,poly B)
    {
        int deg = A.size() + B.size() - 1;
        int limit = FFT_init(deg);
        poly C(limit);
        FFT(A,1,limit),FFT(B,1,limit);
        for(int i=0;i<limit;++i) C[i] = A[i]*B[i];
        // cout<<cnt<<endl;
        FFT(C,-1,limit);
        C.resize(deg);
        return C;
    }
};

using FFT::poly;
using FFT::poly_mul;

int main()
{
    int n;scanf("%d",&n);
    poly a(n+1),b(n+1),c(n+1);
    for(int i=1;i<=n;i++) 
    {
        scanf("%lf",&a[i].x);
        c[n-i].x = a[i].x;
        b[i].x = 1.0/i/i;
    }
    a = poly_mul(a,b,1);
    c = poly_mul(b,c,2);
    for(int i=1;i<=n;i++) printf("%.3lf\n",a[i].x-c[n-i].x);
    return 0;
}

本文作者:艾特玖

本文链接:https://www.cnblogs.com/aitejiu/p/16698827.html

版权声明:本作品采用艾特玖许可协议进行许可。

posted @   艾特玖  阅读(17)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起