2022年9月CSP认证题解 如此编码(k进制),何以包邮?(背包问题),吉祥物投票(珂朵莉树、懒标记、并查集)
T1 如此编码
思路
由公式
和前缀乘积定义
得m=b1+a1×b2+⋅⋅⋅+a1×a2×⋅⋅⋅×an−1×bnm=b_1+a_1\times b_2+···+a_1\times a_2\times···\times a_{n-1}\times b_nm=b1+a1×b2+⋅⋅⋅+a1×a2×⋅⋅⋅×an−1×bn,
上述公式可以提取公共乘项aia_iai,写成m=(bnbn−1⋅⋅⋅b1)am=(b_nb_{n-1}···b_1)_{a}m=(bnbn−1⋅⋅⋅b1)a,类似kkk进制表达,但每一位权的变化倍数不是常数kkk,而是aka_kak,对于b[i]b[i]b[i]的求解,类比10进制转kkk进制,将kkk替换为a[i]a[i]a[i]即可。
代码
void solve() {int n, m;cin >> n >> m;vector<int> a(n), b(n);for (int i = 0; i < n; i++) cin >> a[i];for (int i = 0; i < n; i++) {b[i] = m % a[i];m /= a[i];}for (int i = 0; i < n; i++) cout << b[i] << ' ';cout << '\n';
}
T2 何以包邮?
思路
背包问题,问题等价于求(m−x)(m-x)(m−x)的钱最多买几本书。
代码
void solve() {int n, x; cin >> n >> x;vector<int> a(n);int sum = 0;for (int i = 0; i < n; i++) cin >> a[i], sum += a[i];int v = sum - x;vector<int> dp(v + 1, 0);for (int i = 0; i < n; i++) {for (int j = v; j >= 0; j--) {if (j >= a[i]) {dp[j] = max(dp[j], dp[j - a[i]] + a[i]);}}}cout << sum - dp[v] << '\n';
}
T4 吉祥物投票
思路
看见区间赋值操作且维护区间长度为1e91e91e9,很容易想到珂朵莉树,用set
维护一系列区间。
珂朵莉树模板
- 结构体
node
中v
用mutable
修饰,访问容器set
时迭代器const
修饰,只读,通过该mutable
使得整个结构体只读时该成员仍可变,为后面区间加操作铺垫。 split()
将区间从pos
处断开,并返回以pos
为左端点的迭代器。这里首先调用set
自身的二分方法lower_bound()
找到左端点不小于pos
的区间,找到后判断it->l == pos
,若满足则不需要断开区间,直接返回;否则pos
在前面一个区间中,将该区间断开为node(L, pos - 1, V)
和node(pos, R, V)
,重新插入容器,insert
方法成功插入后返回迭代器,返回该对象。assign()
区间赋值,这里先split(r+1)
获取结束端点是因为(r+1)
可能在begin
指向区间,操作使迭代器begin
失效。之后erase(begin,end)
,插入新区间即可。
struct node {ll l ,r;mutable ll v;node(ll l, ll r, ll v) : l(l), r(r), v(v) {}bool operator<(const node &a) const { return l < a.l; }
};set<node> tree;set<node>::iterator split(ll pos) {auto it = tree.lower_bound(node(pos, 0, 0));if (it != tree.end() && it->l == pos)return it;it--;ll L = it->l, R = it->r, V = it->v;tree.erase(it);tree.insert(node(L, pos - 1, V));return tree.insert(node(pos, R, V)).first;
}void assign(ll l, ll r, ll v) {auto end = split(r + 1), begin = split(l);tree.erase(begin, end);tree.insert(node(l, r, v));
}
部分分做法
用一个数组cnt[]
维护每个作品的得票,在assign
操作时实时更新,这样可以O(1)O(1)O(1)回应操作4,查询每个作品的得票。
对于操作2,3,暴力O(m)O(m)O(m)访问容器内所有段,修改v
值,由于mutable
关键字我们可以直接通过迭代器修改。
对于操作5,开一个map
,暴力O(m)O(m)O(m)访问容器内所有段,统计最大者。
这样可以得45分。
全部分做法
① 首先,用set
维护区间最大值,O(1)O(1)O(1)回应操作5,这个在之前的3月份的第四题通信管理中也用到过,详见我的博客CSP22.3 T4通信系统管理;
② 对于操作2,3,我们并不需要真的修改容器内的值,只需要改变标签和v
值的映射即可,一种懒惰更新方法,开一数组lazy[]
,标签iii所对应的v
值为lazy[i]
,对于操作2,交换lazy[x]
和lazy[y]
;而对于操作2,我们引入并查集,将多个v
值映射到一个懒标记lazy[w]
上,此时标签iii所对应的v
值可能有多个,满足find(v)=lazy[i]
,同时lazy[x]
赋一个从未使用过的数字即可,另外我们维护从懒标记到标签的映射inv[]
。
举个例子:
假设set
中维护了以下段:(1,3,2),(4,5,1),(6,9,3),(10,12,2),此时还未执行过操作2,3,标签和lazy
,fa
层之间的映射为
tag | lazy | fa |
---|---|---|
1 | 1 | 1 |
2 | 2 | 2 |
3 | 3 | 3 |
执行操作3 1 2后,容器内容不变:
tag | lazy | fa |
---|---|---|
1 | 2 | 1 |
2 | 1 | 2 |
3 | 3 | 3 |
这时统计标签为1的段,应该统计val
值满足lazy[1]=2
的段,执行操作2 2 1之后,容器同样不变,而映射变为:
tag | lazy | fa |
---|---|---|
1 | 2 | 2 |
2 | 4 | 2 |
3 | 3 | 3 |
4 |
统计标签为1的段,应统计val
值满足find(val)=lazy[1]=2
的段,即val
值为1,2的段。
坑
有一个地方我调了很久,就是操作5中输出最多得票的编号最小者,这个编号经过层层映射,早已不是容器内v
值,或者find(v)
,而是对应标签inv[find(v)]
的最小者,由于我们插入时已经取了find(v)
,保证v==find(v)
,因此将所有最多得票者的inv[v]
中取最小输出答案。
代码
struct node {int l ,r;mutable int v;node(int l, int r, int v) : l(l), r(r), v(v) {}bool operator < (const node &a) const { return l < a.l; }
};struct pp
{int k, v;bool operator < (const pp& a) const {if (v != a.v) return v > a.v;return k < a.k;}
};set<node> tree; // Chthollty tree
int lazy[200005], cnt[200005], fa[200005];
map<int , int> inv; // 建立标记和标签之间双射
set<pp> all; // 维护区间最值int _find(int x) {if (x == fa[x]) return x;return (fa[x] = _find(fa[x]));
}void _union(int x, int y) {fa[_find(y)] = _find(x);
}set<node>::iterator split(int pos) {auto it = tree.lower_bound(node(pos, 0, 0));if (it != tree.end() && it->l == pos)return it;it--;int L = it->l, R = it->r, V = it->v;tree.erase(it);tree.insert(node(L, pos - 1, V));return tree.insert(node(pos, R, V)).first;
}void assign(int l, int r, int v) {auto end = split(r + 1), begin = split(l);int tv; // 标记可能被操作2合并至另一个标记,fa[]存储真实标记for (auto it = begin; it != end; it++) {tv = _find(it->v);all.erase({tv, cnt[tv]});cnt[tv] -= (it->r - it->l + 1);all.insert({tv, cnt[tv]});}tree.erase(begin, end);tv = _find(v);tree.insert(node(l, r, tv));all.erase({tv, cnt[tv]});cnt[tv] += (r - l + 1);all.insert({tv, cnt[tv]});
}void solve() {int n, m, q; cin >> n >> m >> q;int tot = m;tree.insert(node(1, n, 0));for (int i = 0; i <= max(2 * m, 2 * q); i++) lazy[i] = fa[i] = i;for (int i = 1; i <= m; i++) inv[i] = i;cnt[0] = n;all.insert({0, n}); // 1e9int op, l, r, x, w, y;while (q--) {cin >> op;if (op == 1) {cin >> l >> r >> x;assign(l, r, lazy[x]);}else if (op == 2) {cin >> x >> w;int fx = lazy[x], fy = lazy[w];all.erase({fx, cnt[fx]});all.erase({fy, cnt[fy]});cnt[fy] += cnt[fx]; // 标记lazy[x]弃用all.insert({fy, cnt[fy]});_union(fy, fx);lazy[x] = ++tot, inv[tot] = x; // 创建一个新标记}else if (op == 3) {cin >> x >> y;swap(inv[lazy[x]], inv[lazy[y]]);swap(lazy[x], lazy[y]);}else if (op == 4) {cin >> w;cout << cnt[lazy[w]] << '\n';}else if (op == 5) {int ans = 0, ansi = 0;for (auto it : all) {if (it.k && it.v > ans) {ans = it.v;ansi = inv[it.k];}if (it.k && it.v == ans && inv[it.k] < ansi) ansi = inv[it.k];if (ans > it.v) break;}cout << ansi << '\n';}}
}
2022年9月CSP认证题解 如此编码(k进制),何以包邮?(背包问题),吉祥物投票(珂朵莉树、懒标记、并查集)相关推荐
- CSP认证题解第一题
文章目录 202012-1 期末预测之安全指数 202009-1 称检测点查询 202006-1 线性分类器 201912-1 报数 201909-1 小明种苹果 201903-1 小中大 20181 ...
- 洛谷P1017题解 [NOIP2000 提高组] 进制转换
原文地址:https://luvletter.blog.luogu.org/p1017-ti-jie P1017 [NOIP2000 提高组] 进制转换 题目描述 我们可以用这样的方式来表示一个十进制 ...
- 2019年3月CCF CSP 认证题解第一题
第一题:201903-1 [题目背景] 在数据分析中,最小值最大值以及中位数是常用的统计信息. [题目描述] 老师给了你n个整数组成的测量数据,保证有序(可能为升序或降序),可能存在重复的数据.请统计 ...
- 2021 12月CSP认证心得
考前 差不多考前两个月开始准备,每个周都刷一些csp往年的题目,或者每个周都刷一些算法题,就这样一直到了最后一个周,害,每次考试前都会放纵一下自我,无论啥考试都不例外,最后一个周也松懈了一下下,不过还 ...
- CSP认证题解第二题
文章目录 202012-2 期末预测之最佳阈值 202009-2 风险人群 201909-2 小明种苹果(续) 201903-2 二十四点 201812-2 小明放学 201809-2 买菜 2018 ...
- CCF CSP认证 题解:201703-3 Markdown(Java语言原创)
问题描述 Markdown 是一种很流行的轻量级标记语言(lightweight markup language),广泛用于撰写带格式的文档.例如以下这段文本就是用 Markdown 的语法写成的: ...
- 花神游历各国 题解(小清新线段树/树状数组+并查集)
题面 众所周知,这是一道小清新线段树 然而可以用树状数组水过去且跑得飞快 看到区间开方第一反应肯定是线段树懒标记区间修改之类的,但是这个东西似乎确凿不可维护 所以考虑暴力循环单点修改->T飞 于 ...
- 【题解】洛谷P1066 [NOIP2006TG] 2^k进制数(复杂高精+组合推导)
洛谷P1066:https://www.luogu.org/problemnew/show/P1066 思路 挺难的一道题 也很复杂 满足题目要求的种数是两类组合数之和 r的最多位数m为 w/k(当w ...
- C认证笔记 - 计算机通识 - 进制转换
计算机通识 - 目录指引 计算机通识 1:进制转换 1.0 任务目标 1.1 进制的定义 1.2 各进制间的转换方法 1.2.1 「二进制」转「十进制」 1.2.2 「二进制」转「八进制」 1.2.3 ...
最新文章
- asp.net断点续传技术
- Linux(64位)下OpenBabel 2.4.1、python2.7和Ipython实战(三)
- linux下如何将mysql加入环境变量
- ubuntu下小键盘不能用
- org.apache.ibatis.binding.BindingException原因总结
- Java 查找指定类型的数组元素
- python requests 示例_Python Requests模块的简单示例
- Java SE 6.0编程指南_Java SE 6.0 编程指南(附光盘)
- 如何将瀑布流里的图片加链接_只需5步!魔幻丛林瀑布后期揭秘
- android应用程序开发_深圳app开发公司:跨平台应用程序开发工具有哪些?
- android自定义viewgroup之我也玩瀑布流
- 华三交换机mode是什么意思_POE交换机150米、长距离250米传输是什么意思?
- Tideways+Xhgui搭建非侵入式php监控平台
- angular要多久学会_成为优秀Angular开发者所需要学习的19件事
- webpack+vue解决前端跨域问题
- python学习笔记7-读取pdf并输出到excel
- ​新手到底如何入门PLC?
- vscode下搭建vue项目
- ad7606中文资料_AD7606-6 pdf,AD7606-6中文资料,AD7606-6应用电路-华秋商城
- 隐语义模型(Latent Factor Model, LFM)原理以及代码实现
热门文章
- github-july-回文判断
- 使用deepdive来做因子图推理
- 【TV Picture Quality - 05】TV PQ术语汇总
- 危化行业人员定位系统解决方案,减少人员伤亡-新导智能
- 光环国际PMP:项目经理如何回答“这个项目要多久完成?”
- 一个比较齐全的汉字字库对应拼音 操作实现
- python电脑安装教程_python详细安装教程
- jsp中的java代码怎样调试
- win7备份工具_Win7/10系统如何一键还原系统?一键备份还原系统方法图文教程
- mysql基于SpringBoot小而学在线考试系统毕业设计源码141507