[ZJOI2014][JZOJ3617]力

14 篇文章 3 订阅

题目大意

给出 n 个数qi,设

Fj=i<jqiqj(ij)2i>jqiqj(ij)2

Ei=Fiqi 试求 Ei
1n100000 0<qi<1000000000


题目分析

这是我第一道 FFT ,搞了很久。
我们简化一下

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

显然可以分成前后两部分算,第二部分直接将 q 数组倒过来就和第一部分一样了。
那我们怎么做第一部分呢?令数组bi=1i2(i0) b0=0 ,那么我们就可以将上式变为标准点积
Ansk=i=0kqibki

然后上 FFT 就行了。
时间复杂度 O(nlogn2)


代码实现

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>

using namespace std;

const int N=262144;
const double pi=acos(-1);

struct C
{
    double x,y;
    C (double x0=0,double y0=0){x=x0,y=y0;}
};

C operator+(C a,C b){return C(a.x+b.x,a.y+b.y);}
C operator-(C a,C b){return C(a.x-b.x,a.y-b.y);}
C operator*(C a,C b){return C(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
C operator*(C a,double b){return C(a.x*b,a.y*b);}
C operator/(C a,C b){return C((a.x*b.x+a.y*b.y)/(b.x*b.x-b.y*b.y),(a.y*b.x-a.x*b.y)/(b.x*b.x-b.y*b.y));}
C operator/(C a,double b){return C(a.x/b,a.y/b);}

C f[N],g[N],f_[N],r1[N],r2[N],t[N];
double q[N],ans[N];
int id[N];
int n,n0,l;

void preparation()
{
    l=(int)ceil(log(n0*2)/log(2));
    n=1<<l;
    for (int i=0;i<n;i++)
        for (int j=i,k=0;k<l;j/=2,k++)
            id[i]=(id[i]<<1)+(j&1);
}

void DFT(C *a,int sig)
{
    for (int i=0;i<n;i++)
        t[id[i]]=a[i];
    for (int m=2;m<=n;m*=2)
    {
        int h=m>>1;
        for (int i=0;i<h;i++)
        {
            C w(cos(i*sig*pi/h),sin(i*sig*pi/h));
            for (int j=i;j<n;j+=m)
            {
                C u=t[j],v=w*t[j+h];
                t[j]=u+v;
                t[j+h]=u-v;
            }
        }
    }
    for (int i=0;i<n;i++)
        a[i]=t[i];
    if (sig==-1)
        for (int i=0;i<n;i++)
            a[i]=a[i]/n;
}

void FFT(C *a,C *b,C *c)
{
    DFT(a,1);
    DFT(b,1);
    for (int i=0;i<n;i++)
        c[i]=a[i]*b[i];
    DFT(c,-1);
}

void solve()
{
    g[0]=C(0,0);
    for (int i=1;i<n0;i++)
        g[i]=C(1.0/i/i,0);
    FFT(f,g,r1);
    memset(g,0,sizeof g);
    for (int i=1;i<n0;i++)
        g[i]=C(1.0/i/i,0);
    FFT(f_,g,r2);
    for (int i=0;i<n0;i++)
        ans[i]=r1[i].x-r2[n0-i-1].x;
}

int main()
{
    freopen("force.in","r",stdin);
    freopen("force.out","w",stdout);
    scanf("%d",&n0);
    for (int i=0;i<n0;i++)
    {
        scanf("%lf",&q[i]);
        f[i]=C(q[i],0);
        f_[n0-i-1]=C(q[i],0);
    }
    preparation();
    solve();
    for (int i=0;i<n0;i++)
        printf("%.4lf\n",ans[i]);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值