行路难,行路难,多歧路,今安在?长风破浪会|

hhhqx

园龄:1年8个月粉丝:9关注:14

绝对众数+摩尔投票

一个区间的绝对众数。一个序列中的众数,如果出现次数大于 N÷2 就是绝对众数

摩尔投票:定义两个变量 x,c

  • 从左到右遍历加入,如果加入的 ai 不等于当前变量 x,则 c 减一,否则 c 加一。
  • 若加入时 c=0,则 x=aic=1

最后的如果 x 不是绝对众数,则 A 没有绝对众数,否则 x 为绝对众数。

可以发现摩尔投票只用存储一个二元组,本质是一种二元组合并(或运算),显然支持结合律,所以可以与线段树结合。

模板题:https://www.luogu.com.cn/problem/P2397

模板题代码
#include <bits/stdc++.h>

using namespace std;
using LL = long long;
using PII = pair<int, int>;

const int MAXN = 2e6 + 3;

int n;

PII Merge(PII i, PII j){
  if(i.first == j.first){
    return {i.first, i.second + j.second};
  }
  if(i.second > j.second) return {i.first, i.second - j.second};
  return {j.first, j.second - i.second};
}

int main(){
  cin >> n;
  PII ans = {0, 0};
  for(int i = 1; i <= n; i++){
    PII w;
    cin >> w.first, w.second++;
    ans = Merge(ans, w);
  }
  cout << ans.first;
  return 0;
}

练习题:https://www.luogu.com.cn/problem/CF1514D

线段树和摩尔投票结合。

练习题代码
#include <bits/stdc++.h>

using namespace std;
using LL = long long;
using PII = pair<int, int>;

const int MAXN = 1e6 + 3;

int n, Q, a[MAXN], id[MAXN];
vector<int> p[MAXN];

PII Merge(PII i, PII j){ // 摩尔投票的二元组合并
  if(i.first == j.first){
    return {i.first, i.second + j.second};
  }
  if(i.second > j.second) return {i.first, i.second - j.second};
  return {j.first, j.second - i.second};
}

int Ask(int w, int L, int R){ // 询问 w 在 [L,R] 中的出现次数
  if(p[w].empty()) return 0;
  int l = 0, r = p[w].size() - 1;
  while(l < r){
    int mid = (l + r) >> 1;
    if(p[w][mid] >= L){
      r = mid;
    }else l = mid + 1;
  }
  if(p[w][l] < L || p[w][l] > R) return 0;
  int _l = 0, _r = p[w].size() - 1;
  while(_l < _r){
    int mid = (_l + _r + 1) >> 1;
    if(p[w][mid] <= R){
      _l = mid;
    }else _r = mid - 1;
  }
  if(p[w][_l] < L || p[w][_l] > R) return 0;
  return _l - l + 1;
}

PII tr[MAXN * 4];
void B(int i, int l, int r){ // 线段树
  if(l == r){
    tr[i] = {a[l], 1};
    return;
  }
  int mid = (l + r) >> 1;
  B(i * 2, l, mid), B(i * 2 + 1, mid + 1, r);
  tr[i] = Merge(tr[i * 2], tr[i * 2 + 1]);
}
PII S(int i, int l, int r, int L, int R){
  if(l == L && r == R) return tr[i];
  int mid = (l + r) >> 1;
  PII ret = {0, 0};
  if(L <= mid) ret = Merge(ret, S(i * 2, l, mid, L, min(mid, R)));
  if(mid + 1 <= R) ret = Merge(ret, S(i * 2 + 1, mid + 1, r, max(mid + 1, L), R));
  return ret;
}

int main(){
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> Q;
  for(int i = 1; i <= n; i++){
    cin >> a[i], id[i] = p[a[i]].size(), p[a[i]].push_back(i);
  }
  B(1, 1, n);
  for(int q = 1, l, r; q <= Q; q++){
    cin >> l >> r;
    int x = S(1, 1, n, l, r).first, y = Ask(x, l, r);
    if(y > ((r - l + 1) / 2)){
      cout << 1 + (y - (r - l + 1 - y + 1)) << "\n";
    }else cout << 1 << "\n";
  }
  return 0;
}

练习题:https://www.luogu.com.cn/problem/P4062

这题与摩尔投票无关,用到了很多绝对众数的性质。

做法

枚举绝对众数 x,得到新序列 bi=(1)[aix],需要求出多少个区间满足 bi 之和大于 0

注意到 bi=1 的总量是 n

然后就是简单的了。随便选择一个数据结构支持“区间加”和“区间加公差为 1 的等差数列”和“区间查询”。

练习题代码
#include <bits/stdc++.h>

using namespace std;
using LL = long long;

const int MAXN = 2e6 + 3, V = 5e5 + 3;

struct SGT{
	LL tr[MAXN * 4], lz[MAXN * 4], _lz[MAXN * 4];
	inline void Push(int x, LL w, int l, int d){
		lz[x] += w, _lz[x] += d;
		tr[x] += w * l + 1ll * d * (1 + l) * l / 2;
	}
	inline void Down(int x, int l, int mid, int r){
		Push(x * 2, lz[x], mid - l + 1, _lz[x]);
		Push(x * 2 + 1, _lz[x] * (mid - l + 1) + lz[x], r - mid, _lz[x]);
		lz[x] = 0, _lz[x] = 0;
	}
	void Update(int i, int l, int r, int L, int R, int w, int d){
		if(L <= l && r <= R){
			Push(i, 1ll * d * (l - L) + w, r - l + 1, d);
			return;
		}
		int mid = (l + r) >> 1;
		Down(i, l, mid, r);
		if(L <= mid) Update(i * 2, l, mid, L, R, w, d);
		if(mid + 1 <= R) Update(i * 2 + 1, mid + 1, r, L, R, w, d);
		tr[i] = tr[i * 2] + tr[i * 2 + 1];
	}
	LL Query(int i, int l, int r, int L, int R){
		if(L <= l && r <= R){
			return tr[i];
		}
		int mid = (l + r) >> 1;
		LL ret = 0;
		Down(i, l, mid, r);
		if(L <= mid) ret += Query(i * 2, l, mid, L, R);
		if(mid + 1 <= R) ret += Query(i * 2 + 1, mid + 1, r, L, R);
		return ret;
	}
}tr;

int n, a[MAXN];
vector<int> pos[MAXN];

int main(){
	ios::sync_with_stdio(0), cin.tie(0);
	int cid;
	cin >> n >> cid;
	for(int i = 1; i <= n; i++){
		cin >> a[i], pos[a[i]].push_back(i);
	}
	LL ANS = 0;
	for(int col = 0; col < n; col++){
		vector<int> vt = pos[col], pvt, svt;
		vt.push_back(n + 1);
		for(int i = 0; i < vt.size(); i++){
			pvt.push_back((i == 0 ? vt[i] - 1 : vt[i] - vt[i - 1] - 1));
			svt.push_back(1 - pvt.back() + (svt.empty() ? V : svt.back()));
		}
		for(int i = 0; i < vt.size() - 1; i++){
			int l = svt[i] - 1, r = svt[i] - 1 + pvt[i];
			tr.Update(1, 1, V * 2, l, r, 0, 1), tr.Update(1, 1, V * 2, r + 1, V * 2, r - l + 1, 0);
			ANS += tr.Query(1, 1, V * 2, svt[i] - pvt[i + 1] - 1, svt[i] - 1);
		}
		for(int i = 0; i < vt.size() - 1; i++){
			int l = svt[i] - 1, r = svt[i] - 1 + pvt[i];
			tr.Update(1, 1, V * 2, l, r, 0, -1), tr.Update(1, 1, V * 2, r + 1, V * 2, - (r - l + 1), 0);
		}
	}
	cout << ANS;
	return 0;
}

本文作者:hhhqx

本文链接:https://www.cnblogs.com/huangqixuan/p/18345091

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hhhqx  阅读(24)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起