概述

摩尔投票算法用于求解区间中出现次数严格大于 $len / k$ 的数。

普通摩尔投票

普通摩尔投票只能解决 $k = 2$ 时的问题。

我们先考虑一个暴力的方法。维护当前答案 $x$,和 $x$ 的权值 $c$。依次扫描 $l$ 到 $r$ 的每个数。

假设当前数为 $a$:

  • 如果 $x = a$,那么将 $c$ 加上 $1$
  • 否则,将 $c$ 减去 $1$。如果 $c$ 减去 $1$ 会变成负数,那么将 $x$ 赋值为 $a$,并将 $c$ 设为 $1$

如果次区间存在出现次数严格大于 $len / 2$ 的数,那么它就是 $x$。因为 $x$ 的出现次数严格大于 $len / 2$,其他所有数都不可能在最终把 $c$ 消成负数。

Misra - Gries 算法

回到原问题,现在 $k$ 可能不是 $2$ 了。

出现次数严格大于 $len / k$ 的数最多有 $k - 1$ 个,我们需要维护这 $k - 1$ 个数。记为 $x_1, \ldots, x_{k - 1}$ 和 $c_1, \ldots, c_{k - 1}$。

仿照普通摩尔投票。依次扫描 $l$ 到 $r$ 的每个数。

假设当前数为 $a$:

  • 如果 $x_i = a$,那么将 $c_i$ 加上 $1$
  • 否则,如果存在一个 $c_i = 0$,将 $x_i$ 设为 $a$
  • 否则,将所有 $c_i$ 全部减掉 $1$

我们用反证法证明。假设有一个出现次数大于 $len / k$ 的数 $f$,并不在 $x$ 中。设 $f$ 在区间中的出现次数为 $cnt$。$f$ 被删除了 $cnt$ 次,每次会删除 $k$ 个数,所以一共删除了 $k\cdot cnt$ 个数,但是 $cnt > len / k$,因此 $k \cdot cnt > n$,显然矛盾。

由于我们不依赖元素顺序,因此有交换律和结合律。显然可以用线段树维护区间的投票结果。合并是 $O\left(k\right)$ 的。

单次区间查询复杂度为 $O\left(k \log n\right)$。

也可以支持区间覆盖等修改。

代码

https://codeforces.com/contest/643/problem/G

tp Q;
 
int WITHERING([[maybe_unused]] unsigned long long int TEST_NUMBER) {
   tp n, q, pp; cin >> n >> q >> pp;
   Q = 100 / pp;
   struct S {
      tp len;
      array<tp, 5> v;
      array<tp, 5> c;
      
      S(tp len = 0) : len(len) { v.fill(-1); c.fill(0); }
      
      void add(tp x, tp cnt) {
         for (tp i = 0; i < Q; ++i) {
            if (v[i] == x) { c[i] += cnt; return; }
         }
         for (tp i = 0; i < Q; ++i) {
            if (c[i] == 0) { v[i] = x; c[i] = cnt; return; }
         }
         tp t = min(cnt, *min_element(c.begin(), c.begin() + Q));
         cnt -= t;
         for (auto& i : c) i -= t;
         for (tp i = 0; i < Q; ++i) {
            if (c[i] == 0) { v[i] = x; c[i] = cnt; return; }
         }
      }
      
      static S op(S a, S b) {
         a.len += b.len;
         for (tp i = 0; i < Q; ++i) a.add(b.v[i], b.c[i]);
         return a;
      }
      
      static S e() { return S(); }
      
      static S mapping(pair<tp, tp> t, S s) {
         if (t.first == -1) return s;
         s = S(s.len);
         s.add(t.second, s.len);
         return s;
      }
      
      static pair<tp, tp> composition(pair<tp, tp> s, pair<tp, tp> t) {
         return max(s, t);
      }
      
      static pair<tp, tp> id() { return pair(-1, -1); }
   };
   vector<S> zqr(n);
   for (tp i = 0; i < n; ++i) {
      tp x; cin >> x;
      zqr[i] = S(1);
      zqr[i].add(x, 1);
   }
   lib::Segtree<S, S::op, S::e, pair<tp, tp>, S::mapping, S::composition, S::id> o(zqr);
   for (tp z = 0; z < q; ++z) {
      tp op, l, r; cin >> op >> l >> r;
      --l; --r;
      if (op == 1) {
         tp x; cin >> x;
         o.apply(l, r, pair(z, x));
         continue;
      }
      S p = o.prod(l, r);
      vetp tar;
      for (tp i = 0; i < Q; ++i) {
         if (p.v[i] != -1) tar.push_back(p.v[i]);
      }
      cout << tar.size();
      for (auto& i : tar) cout << ' ' << i;
      cout << '\n';
   }
   return 0;
}