D. Professor Higashikata
D. Professor Higashikata
Josuke is tired of his peaceful life in Morioh. Following in his nephew Jotaro's footsteps, he decides to study hard and become a professor of computer science. While looking up competitive programming problems online, he comes across the following one:
Let be a binary string of length . An operation on is defined as choosing two distinct integers and (), and swapping the characters .
Consider the strings , where is the substring of from to . Define as the concatenation of the strings in that order.
There are updates to the string. In the -th update gets flipped. That is if , then becomes and vice versa. After each update, find the minimum number of operations one must perform on to make lexicographically as large as possible.
Note that no operation is actually performed. We are only interested in the number of operations.
Help Josuke in his dream by solving the problem for him.
——————————————————————
A string is a substring of a string if can be obtained from by the deletion of several (possibly, zero or all) characters from the beginning and several (possibly, zero or all) characters from the end.
A string is lexicographically larger than a string of the same length if and only if the following holds:
- in the first position where and differ, the string has a , and the string has a .
Input
The first line contains three integers , , ().
The next line contains a binary string of length , consisting only of digits and .
The -th line of the next lines contains two integers and ().
The -th line of the next lines contains a single integer ().
Output
Print integers. The -th integer is the minimum number of operations that need to be performed on to get the lexicographically largest possible string in the -th round.
Examples
input
2 2 4
01
1 2
1 2
1
1
2
2
output
0
1
0
1
input
8 6 10
10011010
5 6
2 3
6 8
5 7
5 8
6 8
3
5
6
2
5
2
5
8
4
1
output
2
3
2
2
1
2
2
2
2
2
Note
In the first test case,
Originally, .
After the -st query, becomes and consequently becomes . You don't need to perform any operation as is already the lexicographically largest string possible.
After the -nd query, becomes and consequently becomes . You need to perform operation by swapping and . Consequently, becomes which is the lexicographically largest string you can achieve.
解题思路
假设已经得到了 ,为了使得 的字典序最大,我们应该让 的某个前缀变成全 ,那么我们应该尽可能先让 变为全 ,即通过交换使得 中的 变成 。然后再让 变成全 ,以此类推。
现在假设有 和 满足 ,且存在某个 既满足 ,且满足 。由于我们总是按下标从小到大对子串进行操作,因此如果在 中将 变成了 ,那么当要对 进行操作,此时必然有 ,无需再对 进行交换。因此在 中,我们只关心 的每个下标第一次出现的位置,对于某个下标 ,当在 中第一次出现并被交换成 后,那么之后再次出现也不会再进行交换。
我们只需按照原来的顺序保留 中所有第一次出现的下标来得到 ,必然有 。以第二个样例为例子:
获得 最暴力的做法是遍历每一个 的 ,如果某个下标之前没被选过,那么就记录下来。很明显这样做的时间复杂度是 ,因为重复枚举了被选过的下标,因此可以开个 或用并查集来维护还没有被选的下标,以此快速找到下一个还没被选的下标。这样时间复杂度就优化到 或 。
在执行更新操作前先计算当前 的答案,也就是 的答案(最小的操作次数使得 的字典序最大)。假设原始的 中所有 的个数为 , 的前 个字符中 的个数为 分情况讨论:
- ,那么我们就将 的前 个字符所对应的 的下标,在 中全部交换成 ,那么最小的交换次数就是 。
- ,那么我们将 中所有字符所对应的 的下标全部交换成 ,最小交换次数就是 。
综合一下,答案就是 。
于是对于更新操作,我们只需维护 ,以及 的前缀中 的个数,这里可以用树状数组来实现。
写法的 AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
char str[N];
vector<int> p;
int mp[N];
int tr[N];
int lowbit(int x) {
return x & -x;
}
void add(int x, int c) {
for (int i = x; i <= p.size(); i += lowbit(i)) {
tr[i] += c;
}
}
int query(int x) {
int ret = 0;
for (int i = x; i; i -= lowbit(i)) {
ret += tr[i];
}
return ret;
}
int main() {
int n, m, k;
scanf("%d %d %d %s", &n, &m, &k, str + 1);
set<int> st;
for (int i = 1; i <= n; i++) {
st.insert(i);
}
while (m--) {
int l, r;
scanf("%d %d", &l, &r);
auto t = st.lower_bound(l);
while (t != st.end() && *t <= r) {
p.push_back(*t);
t = st.erase(t);
}
}
for (int i = 1; i <= p.size(); i++) {
int x = p[i - 1];
if (str[x] & 1) add(i, 1);
mp[x] = i;
}
int s = count(str + 1, str + n + 1, '1');
while (k--) {
int x;
scanf("%d", &x);
if (str[x] & 1) {
s--;
if (mp[x]) add(mp[x], -1);
}
else {
s++;
if (mp[x]) add(mp[x], 1);
}
str[x] ^= 1;
int t = min<int>(s, p.size());
printf("%d\n", t - query(t));
}
return 0;
}
并查集写法的 AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
char str[N];
int fa[N];
vector<int> p;
int mp[N];
int tr[N];
int find(int x) {
return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}
int lowbit(int x) {
return x & -x;
}
void add(int x, int c) {
for (int i = x; i <= p.size(); i += lowbit(i)) {
tr[i] += c;
}
}
int query(int x) {
int ret = 0;
for (int i = x; i; i -= lowbit(i)) {
ret += tr[i];
}
return ret;
}
int main() {
int n, m, k;
scanf("%d %d %d %s", &n, &m, &k, str + 1);
for (int i = 1; i <= n + 1; i++) {
fa[i] = i;
}
while (m--) {
int l, r;
scanf("%d %d", &l, &r);
for (int i = find(l); i <= r; i = fa[i]) {
p.push_back(i);
fa[i] = find(i + 1);
}
}
for (int i = 1; i <= p.size(); i++) {
int x = p[i - 1];
if (str[x] & 1) add(i, 1);
mp[x] = i;
}
int s = count(str + 1, str + n + 1, '1');
while (k--) {
int x;
scanf("%d", &x);
if (str[x] & 1) {
s--;
if (mp[x]) add(mp[x], -1);
}
else {
s++;
if (mp[x]) add(mp[x], 1);
}
str[x] ^= 1;
int t = min<int>(s, p.size());
printf("%d\n", t - query(t));
}
return 0;
}
参考资料
Codeforces Round 882 Editorial:https://codeforces.com/blog/entry/117928
Codeforces Round 882 (Div. 2)(A-D,待更新):https://zhuanlan.zhihu.com/p/642093526
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17771708.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效