AtCoder Beginner Contest 315 Ex. Typical Convolution Problem(分治NTT/全在线卷积)

博客围绕给定长为n的序列a求序列f的问题展开。采用分治NTT方法,考虑[l,mid]对[mid+1,r]的贡献,分析了贡献完整和只有一半贡献的情况并分类讨论。此外,还提及官方和群友给出的全在线卷积/半在线卷积解法,最后给出参考代码和乱搞代码。

题目

给定长为n(n<=2e5)的序列a,第i个数ai(0<=ai<998244353)

求序列f,满足式子如下:

eq?f_%7Bk%7D%20%3D%20%5Cleft%5C%7B%5Cbegin%7Bmatrix%7D%201%2C%20k%3D0%20%5C%5C%20a_%7Bk%7D*%5Csum_%7Bi&plus;j%3Cn%7Df_%7Bi%7D*f_%7Bj%7D%281%5Cleq%20k%20%5Cleq%20n%29%20%5Cend%7Bmatrix%7D%5Cright.

思路来源

jiangly代码/力扣群友tdzl2003/propane/自己的乱搞

29d5ac4d4d5f475182f8229299d96bdb.png

题解

分治NTT,考虑[l,mid]对[mid+1,r]的贡献

但是,手玩一下就会发现有个问题

 

举个例子,

1. [l,mid]=[0,1],[mid+1,r]=[2,3],那么右半边f2会加上f0*(f0+f1)+f1*f0,贡献完整

2. [l,mid]=[5,6],[mid+1,r]=[7,8],那么右半边f7会加上f5*(f0+f1)+f6*f0

相当于只有一半贡献,比如有f5*f0,没有f0*f5,

因为考虑f0所在区间对右的贡献时,f5还没算出来

对于第二种情况,贡献就需要乘以2

 

这两种情况会混在一起导致很难算么,答案是不会的

考虑第一次出现贡献完整,不需要*2的项时,

左边两个下标最小,右边下标最大,也就是l+l=r-1,满足2*l<r

由于分治NTT是分治的完整的2的幂次的区间,左右半段等长,

观察不难发现(jiangly代码告诉我们)只有l=0时,才会出现2*l<r

所以,分类讨论两种情况即可

Bonus

官方题解/群友给出了全在线卷积/半在线卷积的解法,更好理解,

一边卷积求第i项,一边维护卷积的前缀和

大概看了看是构造出了一个矩阵,

数字表示该数加入的时候算哪些矩阵,

每个矩阵对应一个边长规模的卷积

15574bc3424d4055bede3f71b277c344.png

从而保证任何时刻均摊都是n(logn)^2,可以考虑以后整理个板子(咕)……

代码1(参考)

时间大概是代码2的一半

l=r处求f[l]的值,卷积的前缀和也是在此处算的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<vector>
using namespace std;
#define ll long long
#define ull unsigned ll
const int N = 1<<20, P = 998244353;
const int Primitive_root = 3;
struct Z{
    int x;
    Z(const int _x=0):x(_x){}
    Z operator +(const Z &r)const{ return x+r.x<P?x+r.x:x+r.x-P;}
    Z operator -(const Z &r)const{ return x<r.x?x-r.x+P:x-r.x;}
    Z operator -()const{ return x?P-x:0;}
    Z operator *(const Z &r)const{ return static_cast<ull>(x)*r.x%P;}
    Z operator +=(const Z &r){ return x=x+r.x<P?x+r.x:x+r.x-P, *this;}
    Z operator -=(const Z &r){ return x=x<r.x?x-r.x+P:x-r.x, *this;}
    Z operator *=(const Z &r){ return x=static_cast<ull>(x)*r.x%P, *this;}
    friend Z Pow(Z, int);
    pair<Z,Z> Mul(pair<Z,Z> x, pair<Z,Z> y, Z f)const{
        return make_pair(
            x.first*y.first+x.second*y.second*f,
            x.second*y.first+x.first*y.second
        );
    }
};
Z Pow(Z x, int y=P-2){
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值