bzoj2194 快速傅立叶之二

Description

请计算C[k]=sigma(a[i]*b[i-k]) 其中 k < = i < n ,并且有 n < = 10 ^ 5。 a,b中的元素均为小于等于100的非负整数。

Input

第一行一个整数N,接下来N行,第i+2..i+N-1行,每行两个数,依次表示a[i],b[i] (0 < = i < N)。

Output

输出N行,每行一个整数,第i行输出C[i-1]。

Sample Input

5

3 1

2 4

1 1

2 4

1 4

Sample Output

24

12

10

6

1

HINT

Source

[ Submit][ Status][ Discuss]



分析:
其实我们之前遇到过类似式子: bzoj3527
题目给出的式子实在太丑了,美化一下:

Ck=n1i=kaibik C k = ∑ i = k n − 1 a i b i − k

化式子吧

ck=n1i=kaibik c k = ∑ i = k n − 1 a i b i − k
=nk1ik=0aibik = ∑ i − k = 0 n − k − 1 a i b i − k
=nk1j=1aj+kbj = ∑ j = 1 n − k − 1 a j + k b j (j=ik) ( j = i − k )

N=nk1,cN=ck N = n − k − 1 , c N ′ = c k

cN=Nj=1an1(Nj)bj c N ′ = ∑ j = 1 N a n − 1 − ( N − j ) b j

aNj=an1(Nj),ai=an1i a N − j ′ = a n − 1 − ( N − j ) , a i ′ = a n − 1 − i

cN=Nj=0aNjbj c N ′ = ∑ j = 0 N a N − j ′ b j

标准卷积,上FFT

tip

好久不写FFT
注意下标从0开始
传入的长度是fn(2的整数幂)

#include<bits/stdc++.h>

using namespace std;

const double pi=acos(-1.0);
const int N=400005;
struct node{
    double x,y;
    node (double xx=0,double yy=0) {
        x=xx; y=yy;
    }
};
node a[N],b[N],omega[N],a_omega[N];
int n,m,fn;

node operator +(const node &a,const node &b) {return node(a.x+b.x,a.y+b.y);}
node operator -(const node &a,const node &b) {return node (a.x-b.x,a.y-b.y);}
node operator *(const node &a,const node &b) {return node(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}

void init(int n) {
    for (int i=0;i<n;i++) {
        omega[i]=node(cos(2.0*i*pi/n),sin(2.0*i*pi/n));
        a_omega[i]=node(cos(2.0*i*pi/n),-sin(2.0*i*pi/n));
    }
}

void FFT(int n,node *a,node *w) {
    int i,j=0,k;
    for (i=0;i<n;i++) {
        if (i>j) swap(a[i],a[j]);
        for (int l=n>>1;(j^=l)<l;l>>=1);
    }
    for (i=2;i<=n;i<<=1) {
        int m=i>>1;
        for (j=0;j<n;j+=i) 
            for (k=0;k<m;k++) {
                node z=a[j+k+m]*w[n/i*k];
                a[j+k+m]=a[j+k]-z;
                a[j+k]=a[j+k]+z;
            }
    }
}

double p[N],q[N],ans[N];

int main()
{
    scanf("%d",&n);
    init(fn);
    for (int i=0;i<n;i++) scanf("%lf%lf",&p[i],&q[i]);

    for (int i=0;i<n;i++) {  //0开始 
        a[i].x=p[n-i-1];     //i--->n-1-i
        b[i].x=q[i];
    }

    fn=1;
    while (fn<=2*n) fn<<=1;
    init(fn);
    FFT(fn,a,omega);
    FFT(fn,b,omega);
    for (int i=0;i<=fn;i++) a[i]=a[i]*b[i];
    FFT(fn,a,a_omega);

    for (int i=0;i<n;i++) 
        ans[n-i-1]=a[i].x/fn;
    for (int i=0;i<n;i++) 
        printf("%d\n",(int)(ans[i]+0.5));

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值