• LOJ6500. 「雅礼集训 2018 Day2」操作(哈希+差分)


    题目链接

    https://loj.ac/problem/6500

    题解

    区间取反 (01) 串的经典套路是差分。我们令 (b_i = a_i { m xor} a_{i - 1})({a_i}) 表示原 (01) 序列),这样每一次对一个长度为 (k) 的区间取反只会使两个 (b_i) 发生变化,不妨设为 (b_x, b_y(x < y)),那么一定满足 (y - x = k)。而我们的目的就是使得差分后数组 (b) 里的所有 (1) 变为 (0)

    为了方便,接下来的分析均只使用差分后的数组 (b)

    首先很显然的是,模 (k) 后值不同的位置绝不会互相影响。因此我们可以将所有位置 (i) 按照模 (k) 的值分组,模 (k) 的值相同的位置属于同一组。那么对于每次询问,有解当且仅当 (b) 数组的每一组在区间 ([l, r + 1]) (由于是差分数组,因此要考虑位置 (r + 1))内都恰好包含偶数个 (1)。奇偶性的判断通常用哈希完成,具体地,我们可以给同一组内所有满足 (b_i = 1) 的位置 (i) 赋一个相同的较大的随机值,不同的组赋不同的值,那么一段区间有解,一定满足区间内所有值的异或和为 (0)。这样,我们就能够在 (O(1)) 的时间内判断区间是否有解。

    接下来考虑如何计算最小操作次数。对于任意的 (j(0 leq j < k)),假设满足 (b_i = 1)(i { m mod} k = j) 的所有位置 (i) 从小到大分别为 (p_1, p_2, cdots, p_{2t - 1}, p_{2t}),那么考虑贪心,我们肯定是从左至右依次选择最近的两个 (p) 消去,因此答案为 (frac{(p_2 - p_1) + (p_4 - p_3) + cdots + (p_{2t} - p_{2t - 1})}{k})。我们可以设法维护这个式子的前缀和。在从左至右依次处理时,由于每一组内从右至左的第奇数个已处理的满足 (b_i = 1)(i) 有正的贡献,第偶数个已处理的满足 (b_i = 1)(i) 有负的贡献,而新的 (i) 加入会导致奇偶性改变,即会发生正负交替,因此每加入一个 (b_i = 1)(i) 时,我们直接将 (i { m mod} k) 的所有位置的贡献和取反再加上 (i),最后再添加到前缀和中即可。

    最后注意由于每次询问 ([l, r]) 时我们要强制使得 (a_{l - 1} = a_{r + 1} = 0),因此区间的边界需要特殊处理。

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

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define X first
    #define Y second
    #define mp make_pair
    #define pb push_back
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    
    typedef long long ll;
    typedef long double ld;
    typedef unsigned int uint;
    typedef pair<int, int> pii;
    typedef unsigned long long ull;
    
    template<typename T> inline void read(T& x) {
      char c = getchar();
      bool f = false;
      for (x = 0; !isdigit(c); c = getchar()) {
        if (c == '-') {
          f = true;
        }
      }
      for (; isdigit(c); c = getchar()) {
        x = x * 10 + c - '0';
      }
      if (f) {
        x = -x;
      }
    }
    
    template<typename T, typename... U> inline void read(T& x, U&... y) {
      read(x), read(y...);
    }
    
    template<typename T> inline bool checkMax(T& a, const T& b) {
      return a < b ? a = b, true : false;
    }
    
    template<typename T> inline bool checkMin(T& a, const T& b) {
      return a > b ? a = b, true : false;
    }
    
    const int N = 2e6 + 10;
    
    int n, k, m, s[N][3], v[N];
    
    char str[N];
    
    ull hash_val[N], pre[N];
    
    inline ull get_random() {
      return (1ull * rand() << 30) + (1ull * rand() << 15) + rand();
    }
    
    int main() {
      read(n, k, m);
      scanf("%s", str + 1), str[0] = '0';
      for (register int i = 0; i < k; ++i) {
        hash_val[i] = get_random();
      }
      for (register int i = 1; i <= n; ++i) {
        if (str[i] ^ str[i - 1]) {
          pre[i] = pre[i - 1] ^ hash_val[i % k];
          s[i][0] = s[i - 1][0] + i - (v[i % k] << 1);
          v[i % k] = i - v[i % k];
        } else {
          pre[i] = pre[i - 1];
          s[i][0] = s[i - 1][0];
        }
        s[i][1] = v[i % k];
        s[i][2] = v[(i + 1) % k];
      }
      for (register int i = 1; i <= m; ++i) {
        int l, r; read(l, r);
        ull t = pre[r] ^ pre[l];
        int res = s[r][0] - s[l][0];
        if (str[l] == '1') {
          t ^= hash_val[l % k];
          res -= l - (s[l][1] << 1);
        }
        if (str[r] == '1') {
          t ^= hash_val[(r + 1) % k];
          res += r + 1 - (s[r][2] << 1);
        }
        printf("%d
    ", !t ? res / k : -1);
      }
      return 0;
    }
    
  • 相关阅读:
    nodejs微服务
    node 操作文件流 fs 同步与异步 流式文件的写入与读取
    node判断文件目录是否存在
    Nodejs 使用 Chrome DevTools 调试 --inspect-brk
    使用ovftool工具实现exsi上主机的导入导出
    redis哨兵
    LVS+Nginx
    nginx的proxy代理缓存
    flanneld启动报错Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
    flanneld启动报错Failed to create SubnetManager: parse first path segment in URL cannot contain colon
  • 原文地址:https://www.cnblogs.com/ImagineC/p/9801026.html
Copyright © 2020-2023  润新知