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 s be a binary string of length n. An operation on s is defined as choosing two distinct integers i and j (1i<jn), and swapping the characters si,sj.

Consider the m strings t1,t2,,tm, where ti is the substring of s from li to ri. Define t(s)=t1+t2++tm as the concatenation of the strings ti in that order.

There are q updates to the string. In the i-th update sxi gets flipped. That is if sxi=1, then sxi becomes 0 and vice versa. After each update, find the minimum number of operations one must perform on s to make t(s) 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 a is a substring of a string b if a can be obtained from b 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 a is lexicographically larger than a string b of the same length if and only if the following holds:

  • in the first position where a and b differ, the string a has a 1, and the string b has a 0.

Input

The first line contains three integers n, m, q (1n,m,q2105).

The next line contains a binary string s of length n, consisting only of digits 0 and 1.

The i-th line of the next m lines contains two integers li and ri (1lirin).

The i-th line of the next q lines contains a single integer xi (1xin).

Output

Print q integers. The i-th integer is the minimum number of operations that need to be performed on s to get the lexicographically largest possible string t(s) in the i-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, t(s)=s(1,2)+s(1,2)=0101.

After the 1-st query, s becomes 11 and consequently t becomes 1111. You don't need to perform any operation as t(s) is already the lexicographically largest string possible.

After the 2-nd query, s becomes 01 and consequently t becomes 0101. You need to perform 1 operation by swapping s1 and s2. Consequently, t(s) becomes 1010 which is the lexicographically largest string you can achieve.

 

解题思路

  假设已经得到了 T=t1+t2++tm,为了使得 T 的字典序最大,我们应该让 T 的某个前缀变成全 1,那么我们应该尽可能先让 t1 变为全 1,即通过交换使得 s 中的 i[l1,r1] 变成 1。然后再让 t2 变成全 1,以此类推。

  现在假设有 titj 满足 i<j,且存在某个 x 既满足 x[li,ri],且满足 x[lj,rj]。由于我们总是按下标从小到大对子串进行操作,因此如果在 ti 中将 sx 变成了 1,那么当要对 tj 进行操作,此时必然有 sx=1,无需再对 sx 进行交换。因此在 T 中,我们只关心 s 的每个下标第一次出现的位置,对于某个下标 x,当在 T 中第一次出现并被交换成 1 后,那么之后再次出现也不会再进行交换。

  我们只需按照原来的顺序保留 T 中所有第一次出现的下标来得到 t,必然有 |t|n。以第二个样例为例子:t=s5s6s2s3\bcancels6s7s8\bcancels5\bcancels6\bcancels7\bcancels5\bcancels6\bcancels7\bcancels8\bcancels6\bcancels7\bcancels8=s5s6s2s3s7s8

  获得 t 最暴力的做法是遍历每一个 ti[li,ri],如果某个下标之前没被选过,那么就记录下来。很明显这样做的时间复杂度是 O(mn),因为重复枚举了被选过的下标,因此可以开个 std::set 或用并查集来维护还没有被选的下标,以此快速找到下一个还没被选的下标。这样时间复杂度就优化到 O(m+nlogn)O(m+n)

  在执行更新操作前先计算当前 T 的答案,也就是 t 的答案(最小的操作次数使得 T 的字典序最大)。假设原始的 s 中所有 1 的个数为 sumt 的前 i 个字符中 1 的个数为 ci 分情况讨论:

  1. sum|t|,那么我们就将 t 的前 sum 个字符所对应的 s 的下标,在 s 中全部交换成 1,那么最小的交换次数就是 sumcsum
  2. sum>|t|,那么我们将 t 中所有字符所对应的 s 的下标全部交换成 1,最小交换次数就是 |t|c|t|

  综合一下,答案就是 min{sum,|t|}cmin{sum,|t|}

  于是对于更新操作,我们只需维护 sum,以及 t 的前缀中 1 的个数,这里可以用树状数组来实现。

  std::set 写法的 AC 代码如下,时间复杂度为 O(m+nlogn+qlogn)

#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 代码如下,时间复杂度为 O(m+n+qlogn)

#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

posted @   onlyblues  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示