绝对众数+摩尔投票
一个区间的绝对众数。一个序列中的众数,如果出现次数大于
摩尔投票:定义两个变量
- 从左到右遍历加入,如果加入的
不等于当前变量 ,则 减一,否则 加一。 - 若加入时
,则 且
最后的如果
可以发现摩尔投票只用存储一个二元组,本质是一种二元组合并(或运算),显然支持结合律,所以可以与线段树结合。
模板题: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
这题与摩尔投票无关,用到了很多绝对众数的性质。
做法
枚举绝对众数
注意到
然后就是简单的了。随便选择一个数据结构支持“区间加”和“区间加公差为 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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步