zoukankan      html  css  js  c++  java
  • 【LOJ6500】操作 题解(差分+贪心+哈希)

    8月17日考试 T3

    题目大意:给定一个$01$序列,每次可以选择一个长度为$k$的区间取反。给定$q$次询问,每次询问$[l,r]$至少需要多少次操作才能使所有数变为$0$。

    考虑差分。因此下面所有讲述都是基于差分数组来讲的。

    设$b[i]=a[i] xor a[i-1]$。如果让$[l,r]$内所有数都变为$0$,我们不妨加上前导$0$和后导$0$,这样我们的目标就变为让$b[l]$和$b[r+1]$变为$0$。

    首先考虑无解的情况。考虑到一个位置$i$仅对与它在模$k$意义下同一剩余系的位置有影响,我们不妨对在同一剩余系中的$1$赋上一个较大的随机数,对于$[l,r]$中的$1$,我们将它们的哈希值异或起来,如果结果不为$0$则无解。因为在差分数组中让区间全变为$0$即为消去一对$1$,如果有奇数个$1$显然是无解的。

    考虑怎么维护对答案的贡献。设$ans[i]$表示$[1,i]$对答案的贡献,$pre[i]$表示在$i$之前的与$i$在同一剩余系的位置对答案的贡献,$nxt[i]$表示$pre[i]$加上$i$对答案的贡献。

    考虑$b[i]=1$且$i mod k=j$的所有位置$p_1,p_2,cdots,p_{2t-1},p_{2t}$,我们贪心每次选择两个相邻最近的$1$,然后消去。这样答案为$frac{(p_2-p_1)+cdots+(p_{2t}-p_{2t-1})}{k}$。我们可以想办法考虑维护这个式子的前缀和。显然从左到右扫的时候第奇数个$1$对答案有负的贡献,第偶数个$1$对答案有正的贡献。因此每新加入一个$1$都会导致奇偶性的改变,我们就把原来的答案取反然后再加上$i$。这样我们就能处理$pre$和$nxt$了。知道了这两个,$ans$也很好求了。于是我们可以$O(n)$预处理,$O(1)$查询。

    然而上述解法中有一点要注意:如果$a[l]$或者$a[r]$为$1$,由于加入了前导和后导$0$,此时$l$上和$r+1$上的位置的差分变成了$1$,而我们要将其变为$0$,所以此时边界要进行特殊处理。具体来讲就是先考虑$[l+1,r]$的贡献,然后再单独考虑$l$和$r+1$的贡献。

    这样我们我们就在$O(n+m)$的时间内解决了这个问题。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 2000005;
    int rd[maxn], ans[maxn], pre[maxn], nxt[maxn];
    int a[maxn], b[maxn], hs[maxn], n, m, l, r, k;
    char ch[maxn];
    inline int read() {
        int x = 0, f = 1;
        char ch = getchar();
        while (!isdigit(ch)) {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (isdigit(ch)) {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    int main() {
        srand(time(0));
        n = read();
        k = read();
        m = read();
        for (int i = 0; i < k; i++) rd[i] = rand() * rand() + rand();
        scanf("%s", ch + 1);
        for (int i = 1; i <= n; i++) {
            a[i] = ch[i] - '0';
            b[i] = a[i] ^ a[i - 1];
        }
        b[n + 1] = a[n + 1] ^ a[n];
        for (int i = 1; i <= n + 1; i++) {
            hs[i] = hs[i - 1];
            pre[i] = (i - k <= 0) ? 0 : nxt[i - k];
            if (b[i])
                nxt[i] = i - pre[i], hs[i] ^= rd[i % k];
            else
                nxt[i] = pre[i];
            ans[i] = ans[i - 1] + nxt[i] - pre[i];
        }
        while (m--) {
            l = read();
            r = read();                                       // b:l-r+1
            int ret = ans[r] - ans[l], flag = hs[r] ^ hs[l];  // l+1-r
            if (a[r] == 1)
                flag ^= rd[(r + 1) % k], ret += (r + 1) - 2 * pre[r + 1];  // change r+1 from 1 to 0
            if (a[l] == 1)
                flag ^= rd[l % k], ret += nxt[l] - (l - nxt[l]);  // change l from 1 to 0
            if (flag != 0)
                printf("-1
    ");
            else
                printf("%d
    ", ret / k);
        }
        return 0;
    }
  • 相关阅读:
    062 Unique Paths 不同路径
    061 Rotate List 旋转链表
    060 Permutation Sequence 排列序列
    059 Spiral Matrix II 旋转打印矩阵 II
    058 Length of Last Word 最后一个单词的长度
    057 Insert Interval 插入区间
    bzoj3527: [Zjoi2014]力
    bzoj2194: 快速傅立叶之二
    bzoj2820: YY的GCD
    bzoj2005: [Noi2010]能量采集
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13546333.html
Copyright © 2011-2022 走看看