做题记录 #1

news/2025/10/6 10:36:14/文章来源:https://www.cnblogs.com/Lightwhite/p/19127219

A. P5721 分治 FFT

Problem Link

Y5 下课程里分治结构有放 Antichain, Tree 两道 Poly 题,故进行了一个学习。

半在线卷积。虽然没学过这个东西,但是其思想是比较经典的。半在线要求每一个 \(f_i\)\(f_{1-i-1}\)\(g\) 卷积而得,发现不同的 \(f\) 对后面的贡献之间无关,可以拆开然后加起来。所以可以拆分成若干个区间分别对后面的东西贡献,考虑使用分治,每次做完左区间就卷积贡献到右区间,这样可以把一个前缀的贡献拆分成 \(\log\) 个区间去计算。分治每层复杂度 \(n\log n\),总复杂度 \(\mathcal{O}(n\log^2 n)\)

之前在学习决策单调性 dp 的高贵分治做法时也有看到类似的东西。它说单调队列做法的应用场景在 dp 转移与前缀具体 dp 值相关时,即有半在线需求时。它的做法也是分治用左区间贡献右区间,或许看到半在线问题就要试图思考拆分贡献分治转化。

Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 1 << 17, kP = 998244353, G = 3, iG = 332748118;
int Pow(int a, int b) {int ret = 1;for (; b > 0; b /= 2) {if (b % 2 == 1)ret = 1ll * ret * a % kP;a = 1ll * a * a % kP;}return ret;
}int n, g[kN], f[kN];
int lim, ts[kN], tmp, a[kN], b[kN];inline void NTT(int *f, int n, int o) {for (int i = 0; i < n; i++)ts[i] > i && (swap(f[i], f[ts[i]]), 0);for (int len = 2; len <= n; len *= 2) {int w = Pow(o == 1 ? G : iG, (kP - 1) / len);for (int l = 0; l < n; l += len)for (int i = l, cur = 1; i < l + len / 2; i++, cur = 1ll * cur * w % kP) {int a = f[i], b = 1ll * cur * f[i + len / 2] % kP;f[i] = (a + b) % kP, f[i + len / 2] = (a - b + kP) % kP;}}
}void Divide(int l, int r) {if (l == r)return;int mid = (l + r) / 2, len = r - l + 1;Divide(l, mid);for (int i = 0; i < len; i++)ts[i] = (ts[i >> 1] >> 1) | ((i & 1) * len / 2);for (int i = l; i <= r; i++)a[i - l] = (i <= mid ? f[i] : 0), b[i - l] = g[i - l];NTT(a, len, 1), NTT(b, len, 1);for (int i = 0; i < len; i++)a[i] = 1ll * a[i] * b[i] % kP;NTT(a, len, -1);for (int i = mid + 1, inv = Pow(len, kP - 2); i <= r; i++)f[i] = (f[i] + 1ll * inv * a[i - l]) % kP;Divide(mid + 1, r);
}int main() {cin.tie(0)->sync_with_stdio(0);cin >> n, lim = 1 << __lg(n - 1) + 1;for (int i = 1; i < n; i++)cin >> g[i];f[0] = 1, Divide(0, lim - 1);for (int i = 0; i < n; i++)cout << f[i] << ' ';return 0;
}

B. ABC269Ex Antichain / C. CF1010F Tree (7)

Problem Link B

Problem Link C

两个使用相同技巧的好题。有可能已经是套路了/xk

Tree 转化一下题意变成求出 \(n\) 个点的二叉树中包含 \(1\) 的连通块个数。然后这两题都可以直接列出一个卷积形式的 DP 式子。

Antichain: \(f_u[x]=[x=1]+(\prod\limits_{v\in son_x}f_v)[x]\),Tree: \(f_u[x]=[x=0]+(f_{l_u}*f_{r_u})[x-1]\),当然可以用生成函数表示。但是复杂度很高,因为 \(F_u\) 项数是 \(sz_u\) 的,复杂度保底一个 \(n^2\)

但是怎么优化这个东西呢?由于它的复杂度和子树大小强相关,所以考虑怎么优化我所计算的东西的子树大小和。考虑重链剖分,每次只暴力合并重链轻儿子的答案,重链放到一起做。这样我合并的信息量是轻儿子子树大小和的。这个东西相当于树上所有节点跳到根经过的轻边数量和。很明显这是 \(\mathcal{O}(n\log n)\) 的。于是算子树的答案将重链和轻子树分开考虑。

对这两道题而言,接下来就是考虑重链上选哪个点/连通块伸入哪个点处,然后暴力把生成函数嵌套式拆开,形成前缀积的和之类的东西,然后分治合并轻儿子多项式 NTT 卷积起来即可。

虽然题目用到了超纲知识 Poly,但是其对子树大小相关信息合并的处理非常巧妙,深刻利用了“深度”和“子树大小”之间的关系。虽然真正需要使用到多项式的地方并不多,但是很多的 DP 转移和其有着一些联系,使用多项式的思考方式来做 DP 的特殊优化或许会有比较好的效果。

Antichain
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <cassert>
#include <iostream>
#include <numeric>
#include <vector>using namespace std;
using LL = long long;
using Poly = vector<int>;
using PII = pair<int, int>;
constexpr int kN = 1 << 18, kP = 998244353, G = 3, iG = 332748118;
inline int Pow(int a, int b) {int ret = 1;for (; b > 0; b /= 2) {b % 2 == 1 && (ret = 1ll * ret * a % kP);a = 1ll * a * a % kP;}return ret;
}namespace POLY {int rev[kN], f[kN], g[kN];inline void NTT(int *f, int n, int o) {for (int i = 0; i < n; i++)i < rev[i] && (swap(f[i], f[rev[i]]), 0);for (int len = 2; len <= n; len *= 2) {int w = Pow(o == 1 ? G : iG, (kP - 1) / len);for (int l = 0; l < n; l += len)for (int i = l, cur = 1; i < l + len / 2; i++, cur = 1ll * cur * w % kP) {int a = f[i], b = 1ll * cur * f[i + len / 2] % kP;f[i] = (a + b) % kP, f[i + len / 2] = (a - b + kP) % kP;}}}inline Poly operator+(Poly a, Poly b) {a.size() < b.size() && (swap(a, b), 0);for (int i = 0; i < b.size(); i++)a[i] = (a[i] + b[i]) % kP;return a;}inline Poly operator*(const Poly &a, const Poly &b) {int sz = a.size() + b.size() - 1, len = 1 << __lg(sz) + 1;for (int i = 0; i < len; i++) {rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * len / 2);f[i] = (i < a.size() ? a[i] : 0), g[i] = (i < b.size() ? b[i] : 0);}NTT(f, len, 1), NTT(g, len, 1);for (int i = 0; i < len; i++)f[i] = 1ll * f[i] * g[i] % kP;NTT(f, len, -1);Poly ret(sz);for (int i = 0, inv = Pow(len, kP - 2); i < sz; i++)ret[i] = 1ll * inv * f[i] % kP;return ret;}
}
using POLY::operator*;
using POLY::operator+;int n;
vector<int> e[kN];
int sz[kN], hson[kN];
void Init(int x, int fa) {sz[x] = 1;for (auto v : e[x]) {if (v == fa)continue;Init(v, x), sz[x] += sz[v];(sz[v] > sz[hson[x]]) && (hson[x] = v);}
}vector<Poly> vec, all;
Poly Mul(int l = 0, int r = vec.size() - 1) {if (l == r)return vec[l];int mid = (l + r) / 2;return Mul(l, mid) * Mul(mid + 1, r);
}
pair<Poly, Poly> Get(int l = 0, int r = all.size() - 1) {if (l == r)return {all[l], Poly{1}};int mid = (l + r) / 2;auto [lm, lp] = Get(l, mid);auto [rm, rp] = Get(mid + 1, r);return {lm * rm, lp + lm * rp};
}
Poly f[kN];
void DP(int x) {for (int i = x; i != 0; i = hson[i]) {for (auto v : e[i])v != hson[i] && (DP(v), 0);}for (int i = x; i != 0; i = hson[i]) {for (auto v : e[i])v != hson[i] && (vec.push_back(f[v]), 0);all.push_back(vec.empty() ? Poly{1} : Mul());vec.clear(), vec.shrink_to_fit();}auto [mul, pre] = Get();f[x] = mul + Poly{0, 1} * pre;all.clear(), all.shrink_to_fit();
}int main() {
#ifndef ONLINE_JUDGEfreopen("input.in", "r", stdin);freopen("output.out", "w", stdout);
#endifcin.tie(0)->sync_with_stdio(0);cin >> n;for (int i = 2, fa; i <= n; i++)cin >> fa, e[fa].push_back(i);Init(1, 0), DP(1);for (int i = 1; i <= n; i++)cout << (i < f[1].size() ? f[1][i] : 0) << '\n';return 0;
}

\(\\\)

Tree
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <cassert>
#include <iostream>
#include <numeric>
#include <vector>using namespace std;
using LL = long long;
using Poly = vector<int>;
using PII = pair<int, int>;
constexpr int kN = 1 << 17, kP = 998244353, G = 3, iG = 332748118;
inline int Pow(int a, int b) {int ret = 1;for (; b > 0; b /= 2) {b % 2 == 1 && (ret = 1ll * ret * a % kP);a = 1ll * a * a % kP;}return ret;
}namespace POLY {int f[kN], g[kN], rev[kN];inline void NTT(int *f, int n, int o) {for (int i = 0; i < n; i++)rev[i] > i && (swap(f[i], f[rev[i]]), 0);for (int len = 2; len <= n; len *= 2) {int w = Pow(o == 1 ? G : iG, (kP - 1) / len);for (int l = 0; l < n; l += len)for (int i = l, cur = 1; i < l + len / 2; i++, cur = 1ll * cur * w % kP) {int a = f[i], b = 1ll * cur * f[i + len / 2] % kP;f[i] = (a + b) % kP, f[i + len / 2] = (a - b + kP) % kP;}}}inline Poly operator+(Poly a, Poly b) {a.size() < b.size() && (swap(a, b), 0);for (int i = 0; i < b.size(); i++)a[i] = (a[i] + b[i]) % kP;return a;}inline Poly operator*(const Poly &a, const Poly &b) {int sz = a.size() + b.size() - 1, len = 1 << __lg(sz - 1) + 1;for (int i = 0; i < len; i++) {rev[i] = (rev[i >> 1] >> 1 | ((i & 1) * len / 2));f[i] = (i < a.size() ? a[i] : 0), g[i] = (i < b.size() ? b[i] : 0);}NTT(f, len, 1), NTT(g, len, 1);for (int i = 0; i < len; i++)f[i] = 1ll * f[i] * g[i] % kP;NTT(f, len, -1);Poly ret(sz);for (int i = 0, inv = Pow(len, kP - 2); i < sz; i++)ret[i] = 1ll * inv * f[i] % kP;return ret;}
}
using POLY::operator*;
using POLY::operator+;int n;
vector<int> e[kN];
int sz[kN], hson[kN];
void Init(int x, int fa) {sz[x] = 1;for (auto v : e[x]) {if (v == fa)continue;Init(v, x), sz[x] += sz[v];(sz[v] > sz[hson[x]]) && (hson[x] = v);}
}Poly f[kN];
vector<Poly> vec;
pair<Poly, Poly> Solve(int l = 0, int r = vec.size() - 1) {if (l == r)return {vec[l], vec[l]};int mid = (l + r) / 2;auto [ml, pl] = Solve(l, mid);auto [mr, pr] = Solve(mid + 1, r);return {ml * mr, pl + ml * pr};
}
void DP(int x, int fa) {for (int i = x, lst = fa; i != 0; lst = i, i = hson[i]) {for (auto v : e[i])(v != hson[i] && v != lst) && (DP(v, i), 0);}for (int i = x; i != 0; fa = i, i = hson[i]) {int son = 0;for (auto v : e[i])(v != hson[i] && v != fa) && (son = v);vec.push_back(f[son] * Poly{0, 1});}f[x] = Solve().second + Poly{1};vec.clear(), vec.shrink_to_fit();
}LL x;
int main() {cin.tie(0)->sync_with_stdio(0);cin >> n >> x, x %= kP;for (int i = 1, u, v; i < n; i++) {cin >> u >> v;e[u].push_back(v), e[v].push_back(u);}Init(1, 0);f[0] = {1}, DP(1, 0);int ans = 0;for (int i = 1, fx = 1, fi = 1; i <= n; i++) {ans = (ans + 1ll * fx * Pow(fi, kP - 2) % kP * f[1][i]) % kP;fx = 1ll * fx * (x + i) % kP, fi = 1ll * fi * i % kP;}cout << ans << '\n';return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/929221.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析:【设计模式-3.5】结构型——装饰器模式

深入解析:【设计模式-3.5】结构型——装饰器模式pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas",…

阿爸阿爸

10.6 口胡: QOJ833. Cells Blocking

Python 数据分析与可视化实战:从数据清洗到图表呈现 - 指南

Python 数据分析与可视化实战:从数据清洗到图表呈现 - 指南2025-10-06 10:29 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !importa…

深度学习优化器算法巧思速览

这一篇博文想写很久了,一直没有下笔,核心原因也是有一些待办的思路在攻关验证。 我们先从一个核心的问题出发, 1. 为什么要研究优化器算法? 它的关联问题:训练为什么要调参,调的是什么参? 如果就这个问题去问各…

完整教程:LangChain完全指南:从入门到精通,打造AI应用开发新范式

完整教程:LangChain完全指南:从入门到精通,打造AI应用开发新范式pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: &quo…

在Windows下使用lucky实现TLS/SSL证书自动化

在Windows下使用lucky实现TLS/SSL证书自动化2024.06.06 TLS和SSL TLS 是传输层安全性协议 (Transport Layer Security) 的缩写 他的前身是 SSL 即安全套接层 (Secure Sockets Layer) 其目的是保障在互联网通信时数据…

NDK开发与实践(入门篇微课视频版)

本书旨在通过深入的理论与丰富的实战案例,引领读者系统学习NDK开发技术。NDK技术以其高安全性、卓越性能和高度复用性,成为现代应用开发的关键技术之一。本书从基本概念出发,全面介绍了NDK开发所需的核心基础知识,…

【深度学习优化算法】02:凸性 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

温州通业建设工程有限公司网站深圳网站建站的公司

Git学习笔记 文章目录 Git学习笔记一、版本控制二、Linux基础命令三、Git的环境配置四、Git的基本理论&#xff08;核心&#xff09;五、Git项目的搭建六、Git文件操作七、使用码云八、IDEA集成git九、Git分支 一、版本控制 什么是版本控制 版本控制&#xff08;Revision contr…

调了很久的代码总结

凡是在循环前面加了特判的东西,都要检查一下循环内部要不要判。 可以再读一遍题来调代码。

在Windows上搭建 EasyTier 公共服务器

在Windows上搭建 EasyTier 公共服务器2024.10.01 关于本教程 本教程将介绍如何在Windows上搭建 EasyTier 公共服务器 以帮助那些无公网的节点进行连接 值得注意的是EasyTier是不区分客户端和服务端的 其本身同时承载 服…

Transformer模型开发从0到1——原理深入与项目实践

本书分为5篇共17章。Transformer模型基础篇(第1~5章)重点介绍Transformer模型框架。把Transformer模型掰开,从最基础的输入出发,按照模型框架,一点一点走进Transformer模型的内部,直到最终的模型输出;Transform…

常州品牌网站建设免费文字变形logo设计

在软件工程中&#xff0c;设计模式是为了解决常见的软件设计问题而形成的一套经典解决方案。这些模式不仅能够帮助开发者提高设计的灵活性和代码的重用性&#xff0c;还能使问题的解决方案更加清晰、易于理解。《设计模式精解&#xff0d;GoF 23种设计模式》一书中所列举的23种…

毕业设计 网站开发简单吗蘑菇街网站模板

最近偶尔有用户反馈某些 HTTP 接口出现超时问题&#xff0c;而 web 服务端的 Trace 监控没有出现 http 返回值为 503 等异常情况。出现这种情况一般是web容器出现问题&#xff0c;客户端连 Arthas是Alibaba开源的Java诊断工具&#xff0c;深受开发者喜爱。 Github&#xff1a;h…

鸿蒙OS基于UniApp的区块链钱包创建实践:打造支持鸿蒙生态的Web3应用#三方框架 #Uniapp

鸿蒙OS&基于UniApp的区块链钱包创建实践:打造支持鸿蒙生态的Web3应用#三方框架 #Uniapppre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !impor…

CF700E

题目大意: 给定一个长为 \(n\) 的字符串 \(S\),你要找到最大的 \(k\),使得存在 \(s_{1} \sim s_{k}\) 使得 \(s_{1}\) 是 \(S\) 子串 且 \(s_{i}\) 在 \(s_{i - 1}\) 中作为子串至少出现两次。 \(n \le 2 \times 10…

价值弥漫:“AI元人文”的场域革命与共生之路

价值弥漫:“AI元人文”的场域革命与共生之路 本文探讨“价值弥漫”作为“AI元人文”核心实现路径的哲学基础与技术内涵,提出从“工具性AI”到“场域性AI”的范式转变。 引言:从“价值对齐”到“价值弥漫”的范式迁移…

手机app设计网站公司网站建设需要什么科目

流程图 一、前期准备 1.1 打开百度智能云官网找到管理中心创建应用 全选文字识别 1.2 保存好AppId、API Key和Secret Key 1.3 找到通用场景文字识别&#xff0c;立即使用 1.4 根据自己需要&#xff0c;选择要开通的项目 二、代码编写 以通用文字识别&#xff08;高精度版&am…

k8s之pod概念

1. pod基本概念 2. pod网络概念 3. pod的生命周期和状态 4. 探针 5. 创建pod 6. 总结‍ 1. pod基本概念Kubernetes 中,Pod 是最小的网络调度单位, 每个pod可以放多个容器(例如可以放多个docke容器在同一个pod中运行…

鸿蒙版Taro 搭建开发环境 - 教程

鸿蒙版Taro 搭建开发环境 - 教程2025-10-06 10:09 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !import…