Solution -「ROI 2018」「LOJ #2850」无进位加法
\(\mathscr{Description}\)
Link.
给定 \(\{a_n\}\),每次令某个 \(a_i\leftarrow a_i+1\),最终得到 \(\{b_n\}\),求 \(\sum b=\bigoplus b\) 时 \(\sum b\) 的最小值。
\(n,\sum\lceil\log_2a_i\rceil\le3\times10^5\)。
\(\mathscr{Solution}\)
很有贪心的味道,我们从 \(a_i=2^{k_i}\) 的情况入手分析。在这一情况下,贪心是平凡的:从低到高扫 bit,当前 bit 最多留一个数,其他的进位。考察此时答案的最高 bit \(h\),可以发现,当 \(\{a_i\}\) 降序排列时,有 \(h=\max_{i=1}^n\{k_i+i-1\}\)。必要性显然,归纳可证充分。
同时,注意到一种简单的规约方式:若 \(a_i\in[2^{k_i},2^{k_i+1})\),令所有 \(a_i\) 二进制上取整,规约到上面这种情况,此时答案的最高 bit 为 \(h+1\)。因此我们可以得到一个思路:求出 \(h\),判断答案最高 bit 能否为 \(h\)。
判断最高 bit?暴力一点,直接搜。设 \(p=\min\arg\max_{i=1}^n\{k_i+i-1\}\),那么答案 \([h-p+2,h]\) 里的 bit 全为 \(1\)(它们把 \([1,p)\) 里的数一一抬走,这些数也应当被删掉),如果 \(h\) 不行,那 \(p\) 就必然被 \(h+1\) 抬走;否则 \(h-p+1\) 可以让 \(a_p\) 变小但无法把它抬走(它是 \(\arg\max\)),怎么办?直接将 \(a_p-2^{h-p+1}\),也即是 \(a_p\) 丢到最高位得到的 \(a_p'\),放回集合里,递归判断最高 bit 能否为 \(h-p\)。
实现上,每个数以 \(1\) 位最高 bit 的后缀才可能被加入 \(\{a_n\}\),所以可以在外部对所有这样的后缀基排,把 \(x\) 加入集合时,在线段树 \(x\) “绝对排名”的位置上做修改。由于线段树只是拿来维护 \(\max\) 和 \(\arg\max\),所以当前不在集合里的数对应排名位置上的值设成极小就好。
然后——既然你发现我都在将实现了——设 \(L=\sum\lceil\log_2a_i\rceil\),我们宣称这个算法的复杂度是 \(\mathcal O((n+L)\log L)\) 的,直接写就完啦。
证明
考虑搜索过程,由于最后能得到答案,所以必然有 $\mathcal O(n)$ 次成功的递归,这部分复杂度是 $\mathcal O(n\log L)$。
关键是回溯的复杂度,在此先描述一下具体一点的算法流程。对于当前的 \(h\),若不能直接选,我们将尝试 \(h+1\)。若 \(h+1\) 已经超过了要求的答案上界,就需要把 \([1,p)\) 里的数全部加回来;否则 \(h+1\) 一旦出手,必然出解(已经选出理论上界了),不用回溯。
对着代码进行神妙观察, 我们发现:每个回溯的 \(x\) 都唯一对应这答案里一个确定为 \(1\) 的 bit(来自上文“……里的 bit 全为 \(1\)” 那句话),答案 bit 数显然不超过 \(\mathcal O((n+L)\log L)\)!因此,回溯的总复杂度为 \(\mathcal O((n+L)\log L)\),算法复杂度亦为此。 \(\square\)
\(\mathscr{Code}\)
/*+Rainybunny+*/
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
typedef std::pair<int, int> PII;
#define fi first
#define se second
const int MAXN = 3e5, IINF = 0x3f3f3f3f;
int n, mxl, tot, low[MAXN + 5], ord[MAXN + 5];
std::vector<int> dgt[MAXN + 5], rnk[MAXN + 5], own[MAXN + 5];
PII ref[MAXN + 5];
std::set<int> alv;
bool ans[MAXN * 2 + 5];
struct SegmentTree {
PII mx[MAXN << 2];
int tag[MAXN << 2];
inline void pushup(const int u) {
mx[u] = std::max(mx[u << 1], mx[u << 1 | 1]);
}
inline void pushad(const int u, const int k) {
mx[u].fi += k, tag[u] += k;
}
inline void pushdn(const int u) {
if (tag[u]) {
pushad(u << 1, tag[u]), pushad(u << 1 | 1, tag[u]);
tag[u] = 0;
}
}
inline void build(const int u, const int l, const int r) {
mx[u] = { -IINF, l };
if (l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
inline void modify(const int u, const int l, const int r,
const int ml, const int mr, const int k) {
if (ml > mr) return ;
if (ml <= l && r <= mr) return pushad(u, k);
int mid = l + r >> 1; pushdn(u);
if (ml <= mid) modify(u << 1, l, mid, ml, mr, k);
if (mid < mr) modify(u << 1 | 1, mid + 1, r, ml, mr, k);
pushup(u);
}
} sgt;
inline void update(const int rk, const int type) {
if (~type) alv.insert(rk);
else alv.erase(rk);
sgt.modify(1, 1, tot, rk, rk, type * (IINF + dgt[ref[rk].fi][ref[rk].se]));
sgt.modify(1, 1, tot, 1, rk - 1, type);
}
inline bool solve(const int lim) {
if (sgt.mx[1].fi < 0) return true;
if (sgt.mx[1].fi >= lim) return false;
int tar = sgt.mx[1].fi, rk = sgt.mx[1].se; PII id = ref[rk];
update(rk, -1);
if (id.se) update(rnk[id.fi][id.se - 1], 1);
std::vector<int> back;
while (alv.size() && *alv.rbegin() > rk) {
back.push_back(*alv.rbegin()), update(*alv.rbegin(), -1);
}
rep (i, 0, back.size()) ans[tar - i] = true;
if (solve(tar - back.size())) return true;
ans[tar - back.size()] = false;
if (id.se) update(rnk[id.fi][id.se - 1], -1);
if (tar + 1 < lim) {
ans[tar + 1] = true;
assert(solve(tar - back.size() + 1));
return true;
} else {
update(rk, 1);
for (int u: back) update(u, 1);
return false;
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0);
std::cin >> n;
rep (i, 1, n) {
static std::string str; std::cin >> str;
std::reverse(str.begin(), str.end());
int len = str.size(); mxl = std::max(mxl, len);
rep (j, 0, len - 1) if (str[j] == '1') {
dgt[i].push_back(j), own[j].push_back(i);
}
}
rep (i, 0, mxl - 1) {
ord[0] = 0;
for (int j: own[i]) ord[++ord[0]] = j;
std::sort(ord + 1, ord + ord[0] + 1,
[](const int u, const int v) { return low[u] < low[v]; });
rep (j, 1, ord[0]) {
rnk[ord[j]].push_back(low[ord[j]] = ++tot);
ref[tot] = { ord[j], rnk[ord[j]].size() - 1 };
}
}
sgt.build(1, 1, tot);
rep (i, 1, n) update(rnk[i].back(), 1);
assert(solve(IINF));
int fin = 0;
rep (i, 0, mxl + n + 1) if (ans[i]) fin = i;
per (i, fin, 0) std::cout << ans[i];
std::cout << '\n';
return 0;
}
Solution -「ROI 2018」「LOJ #2850」无进位加法的更多相关文章
- LOJ #2542. 「PKUWC 2018」随机游走(最值反演 + 树上期望dp + FMT)
写在这道题前面 : 网上的一些题解都不讲那个系数是怎么推得真的不良心 TAT (不是每个人都有那么厉害啊 , 我好菜啊) 而且 LOJ 过的代码千篇一律 ... 那个系数根本看不出来是什么啊 TAT ...
- LOJ #2802. 「CCC 2018」平衡树(整除分块 + dp)
题面 LOJ #2802. 「CCC 2018」平衡树 题面有点难看...请认真阅读理解题意. 转化后就是,给你一个数 \(N\) ,每次选择一个 \(k \in [2, N]\) 将 \(N\) 变 ...
- LOJ #2541. 「PKUWC 2018」猎人杀(容斥 , 期望dp , NTT优化)
题意 LOJ #2541. 「PKUWC 2018」猎人杀 题解 一道及其巧妙的题 , 参考了一下这位大佬的博客 ... 令 \(\displaystyle A = \sum_{i=1}^{n} w_ ...
- LOJ #2540. 「PKUWC 2018」随机算法(概率dp)
题意 LOJ #2540. 「PKUWC 2018」随机算法 题解 朴素的就是 \(O(n3^n)\) dp 写了一下有 \(50pts\) ... 大概就是每个点有三个状态 , 考虑了但不在独立集中 ...
- LOJ #2538. 「PKUWC 2018」Slay the Spire (期望dp)
Update on 1.5 学了 zhou888 的写法,真是又短又快. 并且空间是 \(O(n)\) 的,速度十分优秀. 题意 LOJ #2538. 「PKUWC 2018」Slay the Spi ...
- LOJ#2351. 「JOI 2018 Final」毒蛇越狱
LOJ#2351. 「JOI 2018 Final」毒蛇越狱 https://loj.ac/problem/2351 分析: 首先有\(2^{|?|}\)的暴力非常好做. 观察到\(min(|1|,| ...
- Loj #6069. 「2017 山东一轮集训 Day4」塔
Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...
- Loj #6073.「2017 山东一轮集训 Day5」距离
Loj #6073.「2017 山东一轮集训 Day5」距离 Description 给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\) ...
- Loj 6068. 「2017 山东一轮集训 Day4」棋盘
Loj 6068. 「2017 山东一轮集训 Day4」棋盘 题目描述 给定一个 $ n \times n $ 的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置 $ (x, y),(u, ...
- 【LOJ#6066】「2017 山东一轮集训 Day3」第二题(哈希,二分)
[LOJ#6066]「2017 山东一轮集训 Day3」第二题(哈希,二分) 题面 LOJ 题解 要哈希是很显然的,那么就考虑哈希什么... 要找一个东西可以表示一棵树,所以我们找到了括号序列. 那么 ...
随机推荐
- MySql5.7及以上 ORDER BY 报错问题
一.问题 本人使用的MySql版本是8.0的 当MySql5.7及以上的版本执行带有 ORDER BY 的SQL语句时可能会报错. 例如,执行以下mysql语句: SELECT id, user_id ...
- 管中窥豹----从String Intern中观察.NET Core到.NET 8 托管堆的变迁
简介 https://www.cnblogs.com/lmy5215006/p/18494483 在此文中,研究.NET String底层结构时,我所观察到的情况与<.NET Core底层入门& ...
- flask 中的request【转载】
每个框架中都有处理请求的机制(request),但是每个框架的处理方式和机制是不同的,为了了解flask的request中都有什么东西,首先我们要写一个前后端的交互 基于HTML+Flask 写一段前 ...
- KnowledgeManagement
知识管理建议 总则 总参 从无知到有知 资料收集的习惯 发表是最好的记忆 Wiki 使用 建议: Blog 写作 Discuss 搜索技巧 回复:Yibie的知识管理流程与工具选择 一.个人知识管理的 ...
- Avalonia UI 中 Styles 与 ControlTheme 的区别
目录 目录 介绍 使用方式 全局主题 (Global Theme) 局部主题 (Local Theme) 控件主题 (ControlTheme) 问题描述 问题分析 问题1 区别 问题2 重写Temp ...
- Django消息队列之django-rq
github:https://github.com/rq/django-rq RQ(Redis Queue),人如其名,用 redis 做的队列任务 redis ,众所周知, 它的列表可以做队列,rq ...
- Django之项目部署
1.线上部署一般会使用https的方式进行部署,本身django框架是不支持的,所以需要... 1)安装扩展 pip install django-extensions django-werkzeug ...
- 【Java基础】-- instanceof 用法详解
1. instanceof关键字 如果你之前一直没有怎么仔细了解过instanceof关键字,现在就来了解一下: instanceof其实是java的一个二元操作符,和=,<,>这些是类似 ...
- JAVA WEB和Tomcat各组件概念
概述 本篇文章是https://juejin.cn/post/7055306172265414663,这篇文章的再总结,剔除了与Java安全研究没太大关系的内容,对JAVAWEB中的Servlet.F ...
- 德承工控机DA-1000 RS-485串口设置
由于一般情况下调试串口常使用RS-485转USB接口来进行调试,但是USB接口在长时间的调试下,接口容易松动,通讯也比较不稳定容易中断,所以改为DB9接口的RS-485来调试,稳固不松脱.抗干扰能力强 ...