[Luogu P4721] 【模板】分治 FFT

7 篇文章 0 订阅
洛谷传送门

题目背景

也可用多项式求逆解决。

题目描述

给定长度为 n1 n − 1 的数组 g[1],g[2],..,g[n1] g [ 1 ] , g [ 2 ] , . . , g [ n − 1 ] ,求 f[0],f[1],..,f[n1] f [ 0 ] , f [ 1 ] , . . , f [ n − 1 ] ,其中 f[i]=ij=1f[ij]g[j] f [ i ] = ∑ j = 1 i f [ i − j ] g [ j ]

边界为 f[0]=1 f [ 0 ] = 1 。答案模 998244353 998244353

输入输出格式

输入格式:

第一行一个正整数 n n

第二行共 n1 个非负整数 g[1],g[2],..,g[n1] g [ 1 ] , g [ 2 ] , . . , g [ n − 1 ] ,用空格隔开。

输出格式:

一行共 n n 个非负整数,表示 f[0],f[1],..,f[n1] 998244353 998244353 的值。

输入输出样例

输入样例#1:
4
3 1 2
输出样例#1:
1 3 10 35
输入样例#2:
10
2 456 32 13524543 998244352 0 1231 634544 51
输出样例#2:
1 2 460 1864 13738095 55389979 617768468 234028967 673827961 708520894

说明

2n105 2 ≤ n ≤ 10 5

0g[i]<998244353 0 ≤ g [ i ] < 998244353

解题分析

这道题后面的值由一部分前面的贡献得到, 可以使用cdq分治的思想分治计算。 具体而言, 对于一个区间 [l,r] [ l , r ] , 我们先递归计算 [l,mid] [ l , m i d ] 的答案, 再计算 [l,mid] [ l , m i d ] [mid+1,r] [ m i d + 1 , r ] 的贡献, 即对应 g[1rl+1] g [ 1 ∼ r − l + 1 ] f[lmid] f [ l ∼ m i d ] 的卷积, 最后计算 [mid+1,r] [ m i d + 1 , r ] 的答案。

复杂度 O(Nlog2N) O ( N l o g 2 N )

然而FFT被卡精度了, 只好用NTT代替…

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#define ll long long 
#define G 3ll
#define Ginv 332748118ll
#define MOD 998244353ll
#define R register
#define W while
#define IN inline
#define gc getchar()
#define MX 400050
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
IN ll fpow(ll now, ll tim)
{
    ll ret = 1;
    for (R unsigned i = 0; i <= 31; ++i, now = now * now % MOD)
    if(tim & (1 << i)) ret = ret * now % MOD;
    return ret;
}
ll a[MX], b[MX], mul[MX], ans[MX];
int len, rev[MX];
IN void NTT(ll *dat, const int &tot, R bool typ)
{
    for (R int i = 0; i < tot; ++i) if(rev[i] < i) std::swap(dat[i], dat[rev[i]]);
    R int now, cur, bd, step, seg;
    ll tar1, tar2, base, deal;
    for (seg = 1; seg < tot; seg <<= 1)
    {
        base = fpow(typ ? G : Ginv, (MOD - 1) / (seg << 1)); step = seg << 1;
        for (now = 0; now < tot; now += step)
        {
            deal = 1, bd = now + seg;
            for (cur = now; cur < bd; ++cur, deal = deal * base % MOD)
            {
                tar1 = dat[cur], tar2 = dat[cur + seg] * deal % MOD;
                dat[cur] = (tar1 + tar2) % MOD; dat[cur + seg] = ((tar1 - tar2) % MOD + MOD) % MOD;
            }
        }
    }
    if(typ) return;
    ll inv = fpow(tot, MOD - 2);
    for (R int i = 0; i < tot; ++i) dat[i] = dat[i] * inv % MOD;
}
void cdq(R int lef, R int rig)
{
    if(lef == rig) return;
    R int bd, tot = 1, lg = 0, siz = rig - lef + 1, mid = lef + rig >> 1;
    cdq(lef, mid);
    bd = siz << 1;
    W (tot <= bd) tot <<= 1, ++lg;
    for (R int i = 0; i < tot; ++i)
    rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1)),
    a[i] = b[i] = 0;
    for (R int i = lef; i <= mid; ++i) a[i - lef] = ans[i];
    for (R int i = 0; i < siz; ++i) b[i] = mul[i];
    NTT(a, tot, 1), NTT(b, tot, 1);
    for (R int i = 0; i < tot; ++i) a[i] = a[i] * b[i] % MOD;
    NTT(a, tot, 0);
    for (R int i = mid + 1; i <= rig; ++i)
    ans[i] = (ans[i] + a[i - lef - 1] % MOD + MOD) % MOD;
    cdq(mid + 1, rig);
}
int main(void)
{
    in(len); --len; ans[0] = 1;
    for (R int i = 0; i < len; ++i) in(mul[i]);
    cdq(0, len);
    for (R int i = 0; i <= len; ++i) printf("%lld ", ans[i]);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值