首页 > 其他分享 >闲话 23.1.6

闲话 23.1.6

时间:2023-01-06 21:22:06浏览次数:59  
标签:##_ int 闲话 mid len ++ 23.1 卷积

闲话

没自觉地发现活干不完
菜菜菜

今日推歌:《Marine Bloomin’》八王子P×鹿乃

昨天讲解是不是复读含量太高了?

推荐阅读:活下去了 by EntropyIncreaser

人会约束自己。因为爱是永恒,是奉献,是克制,是善良。因此,人在发现自己越矩之时感到愧疚,更因为过去的愧疚踟蹰不前。
其实呢,其实呢,人也是可以为了私心而活的。爱也可以是冲动,是欲望,是所取。想要看到“花海盛开,燕子归来”,因为当人暂时穿越痛苦和罪恶,单纯地欣赏美的时候,事物的美丽才有它本来的归宿。

半在线卷积(分治 FFT)

突然发现我没有详细讲过这个东西的基本原理。这里提到了优化复杂度的 \(k\) 叉内容,这里则是有个实现的板子。本文主要讲一下基础的原理,照着 \(O(n\log^2 n)\) 的做法说。算是对浅谈幂级数的补充吧。

回顾多项式指数函数。已经熟知使用多项式牛顿迭代的方式求解的 \(O(n\log n)\) 的方法,其复杂度也是比较优秀。然而考虑到多项式牛顿迭代的过程中需要大量的 FFT 操作,其常数并不是很优秀。半在线卷积就提供了一种 \(O(n\log^2 n)\) 的小常数做法,在 OI 数据范围内的时间开销常常优于正常实现的牛顿迭代法。

首先我们需要了解半在线卷积所求解的对象。我们假设存在一个序列 \(\langle f_i \rangle\),其第 \(n\) 项系数按如下方式给定:

\[f_n = c_n \times \sum_{i=0}^{n-1}f_ig{n-i} \]

其中序列 \(\langle g_i \rangle\) 是给定的一个序列。

我们观察每个元素 \(f_i\),可以发现其会且仅会向 \(f_j\text{ s.t. }j > i\) 的项贡献。这类特殊的性质不禁令我们想起一些关于特殊贡献方式的 DP 问题,具体地说,贡献与时间相关的一类题目。这类题目中,我们通常会抽象出多层偏序,随后用时间序列上分治的方法固定贡献对象求解。不难想到借用 CDQ 分治的思想。

我们对序列进行如 CDQ 的分治,也就是在对应的平衡树上作中序遍历。每次遍历到一个节点时,发现只有左子树对右子树的贡献。因此可以用已知的左子树和 \(\langle g_i \rangle\) 作卷积,取结果的后半部分作为左子树对右子树的贡献。

容易发现这样做的时间复杂度为 \(O(n \log^2 n)\)。

一份 P4721 的实现
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
const int N = 3e5 + 10, mod = 998244353, g = 3;
using poly = vector<int>;
int n, m;

int qp(int a, int b) {
    int ret = 1;
    while (b) {
        if (b & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    } return ret;
}

const int L = 1 << 21;
int w[2][L];
int IUR() {
    w[0][0] = w[1][0] = 1;
    int w0 = qp(g, (mod - 1) / L);
    for (int i = 1; i < L; ++ i) w[1][i] = w[0][L - i] = 1ll * w[1][i - 1] * w0 % mod;
    return w0;
} int dmy = IUR();

int trs[L];
int initrs(int len) {
    int limit = 1; while (limit < len) limit <<= 1;
    for (int i = 0; i < limit; ++ i) trs[i] = (trs[i >> 1] >> 1) | ((i & 1) ? limit >> 1 : 0);
    return limit;
} 

void NTT(poly & A, int len, int type) {
    A.resize(len);
    for (int i = 0; i < len; ++ i) if (i < trs[i]) swap(A[i], A[trs[i]]);
    for (int mid = 1; mid < len; mid <<= 1) {
        for (int i = L / (mid << 1), j = 0; j < len; j += (mid << 1)) {
            for (int k = 0; k < mid; ++ k) {
                int x = A[j + k], y = 1ll * A[j + k + mid] * w[type][i * k] % mod;
                A[j + k] = (x + y) % mod;
                A[j + k + mid] = (x - y + mod) % mod;
            }
        }
    } if (type == 1) return;
    int tmp = qp(len, mod - 2);
    for (int i = 0; i < len; ++ i) A[i] = 1ll * A[i] * tmp % mod;
}

void CDQ_FFT(poly & F, poly & G, int l, int r) {
    if (l == r) { if (!l) F[l] = 1; return; }
    int mid = l + r >> 1;
    CDQ_FFT(F, G, l, mid);
    static poly A, B; A.clear(), B.clear();
    A.resize(mid-l+1), B.resize(r-l+1);
    rep(i,l,mid) A[i-l] = F[i];
    rep(i,1,r-l) B[i-1] = G[i];
    int len = initrs(r - l + 1);
    NTT(A, len, 1); NTT(B, len, 1);
    for (int i = 0; i < len; ++ i) A[i] = 1ll * A[i] * B[i] % mod;
    NTT(A, len, 0);
    for (int i = mid + 1; i <= r; ++ i) F[i] = (F[i] + A[i - l - 1]) % mod;
    CDQ_FFT(F, G, mid + 1, r);
}

signed main() {
    cin >> n;
    static poly F(n), G(n);
    for (int i = 1; i < n; ++ i) cin >> G[i];
    CDQ_FFT(F, G, 0, n-1);
    for (int i = 0; i < n; ++ i) cout << F[i] << ' ';
}

然后我们有了这套理论就能拿去做一些模板了。依稀记得那天晚上一点多看 EI 省选课时 EI 挑幸运观众上去写 ln exp 和逆的半在线卷积形式

我们就以多项式指数函数为例。给定一个幂级数 \(F(x)\),我们知道我们需要的是 \(G(x) = \exp F(x)\)。下面就是经典的”盯着递归定义式看半个小时想出来如何比对系数“的时间。其实一般就是两边求导,有时还能得到线性递推(

对两边求导得到

\[G'(x) = F'(x) \exp F(x) = F'(x)G(x) \]

这就可以比对系数了。我们对两边提取第 \(n\) 项系数得到

\[(n + 1) G[n + 1] = \sum_{i=0}^n (i + 1) F[i + 1] G[n - i] \]

我们把相同项移到同一边去,得到

\[G[n] = \frac{1}{n} \sum_{i=1}^n i F[i] G[n - i] \]

直接做即可。

具体的实现见 qwaszx 的博客

标签:##_,int,闲话,mid,len,++,23.1,卷积
From: https://www.cnblogs.com/joke3579/p/chitchat230106.html

相关文章

  • the fourteenth——20223.1.6
    #include<stdio.h>intmain(){ 3,4,5;//这是一条语句 //把上面这条语句的值赋值给变量a inta=(3,4,5); printf("a=%d\n",a);}输出结果:a=5因为a的值是整......
  • 关系运算符、逻辑运算符——the thirteenth——2023.1.5
    关系运算符  在C语言中=是赋值的意思,而==才是等于的意思  逻辑运算符一共有三种:&&(并且)、||(或者)、!(非)年龄:取值16-50岁。身高:取值150cm-190cm。身材:1-火辣;2-......
  • 2023.1.6 DP 学习日志
    今天还是学DP,干了两道题1.最长上升子序列II(AcWing.896)数据加强版的最长上升子序列不能直接DP,还得二分(其实有点像贪心)比较简单思路就不写了。其实我WA了五次code:#inc......
  • 2023.1.06 java打印杨辉三角(二维数组)
    publicclassyanghui{publicstaticvoidmain(String[]args){int[][]yanghui=newint[10][];for(inti=0;i<yanghui.length;i++){......
  • 23 年 1 月随笔&闲话
    1.5:对“填鸭式教育”的一些看法下文内容可能有些敏感,但是我就要写。我不叛逆也总会有叛逆的人的。1月2日,当我看到学校发出的网课计划后,我沉没了。手机上那张排得满......
  • 2023.1.05 java实现冒泡排序
    自己的思路:publicclassmaopaopaixu{publicstaticvoidmain(String[]args){int[]arr={24,64,26,89,45};inttmp=0;for(in......
  • 2023.1.4
    昨天题难大家都差不多,今天就160倒数第二(还是策略问题,感觉都不会考试和调代码了。。。签完到就去写最难写的计算几何,最后才开t1,发现很简单,但是没什么时间了。最后写完calc......
  • 闲话 23.1.4
    闲话截至本文撰写时,我多项式那篇已经写了15k了,而我甚至才刚介绍到分式分解今日推歌是《夜行者们》洛天依/闹闹丶!好今天又是水水水(杂题P5219计数\(n\)个点、最大......
  • 如何分享自己写的东西、赋值运算符、算术运算符——the twelfth——2023.1.3
    分享自己写的东西给朋友在vs中生成Relese在Relese文件中找到exe文件分享即可但是一些小的文件会直接执行return0,所以需要将return0前加一个成getchar(),变成待定状态。......
  • 2023.1.3
    本来今天是不想写得,但是明天不练车,所以说还是写了吧,嘿嘿。碎碎念:今天阳光明媚,心情很不错!去办理身份证,非常顺利,拍身份证之前洗了一把脸,冲拍照的小哥哥要的卫生纸,最后扔垃圾没......