I Array
题目简意:
一个n个数的整数数组,子区间长度大于等于一,多少个子区间有众数。
Boyer-Moore 投票算法:
时间复杂度O(n),空间复杂度O(1).
基础理解:
历数组nums 中的所有元素,遍历到元素 x 时,进行如下操作:
如果count=0,则将 x 的值赋给candidate,否则不更新candidate 的值;
如果 x=candidate,则将 count 加 1,否则将count 减 1。
遍历结束之后,candidate的值可能为主要元素。(一般适用于一定存在众数,candidate的值就是主要元素了)
回到Array:
参考代码:
#include <cstdio>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
const int N = 1000000;
vector<int> place[N + 5];
int main()
{
int t = 1, n;
scanf("%d", &t);
while(t--)
{
int x;
scanf("%d", &n);
for (int i = 1; i <= n;i++)
{
scanf("%d", &x);
place[x].push_back(i);
}
long long ans = 0;
for (int num = 0; num <= N; num++)
{
if(!place[num].size())
continue;
place[num].push_back(n + 1);
long long cnt = 0, tot = 0;
//cnt表示了当前sum出现的次数,为次数差分数组dif的前缀和
map<int, long long> dif;
//第一个众数数字前的情况,共计 1-place[num][0] 个
int minimum = 1 - place[num][0], now = 1 - place[num][0];
dif[1]--;
dif[now]++;
//now~0 的数全部出现一次
for (int i = 0; i + 1 < place[num].size(); i++)
{
cnt += dif[now];//加上当前一位的值即可
tot += cnt;
ans += tot;
now++;
dif[now]++;//当前的sum次数增加1
dif[now + 1]--;
int delta = place[num][i + 1] - place[num][i] - 1;
for (; delta > 0 && now > minimum;delta--)
{
tot -= cnt;//先把当前的sum对应次数挖掉
ans += tot;
cnt -= dif[now - 1];//连带修改
now--;
dif[now]++;
dif[now + 1]--;
}
if(delta>0)//跌穿,直接重来
{
dif[minimum]--;
minimum -= delta;
dif[minimum]++;
now = minimum;
cnt = 0;
tot = 0;
}
}
}
printf("%lld\n", ans);
for (int i = 0; i <= N; i++)
place[i].clear();
}
return 0;
}
参考代码 链接
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e6+10;
vector<int>v[maxn];//记录每个众数出现的位置
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; cin>>T;
while(T--){
int n; cin>>n;
vector<int>a(n+1);
unordered_set<int>s; //记录有哪些众数
for(int i=1; i <= n; i++){
cin>>a[i]; v[a[i]].push_back(i); s.insert(a[i]);
}
LL ans = 0;
for(int x : s){//枚举每个数作为众数
LL res = 0, sum = 0;//x的答案贡献res,当前前缀和sum
unordered_map<int,int>f1,f2; //前缀和为sum的点有f1[sum]个, f2为f1差分数组
v[x].push_back(n+1); //防止溢出
LL k = 0, minn = 0; //枚举众数的每个位置k,历史前缀最小值minn
for(int j = 1; j <= n; j++){//枚举每一段-1的起始位置
if(j > v[x][k])k++;
if(a[j]!=x && sum==minn){//区间[j,v[x][k]-1]都是-1
LL len = v[x][k]-1-j;
f2[sum-len]++; f2[sum+1]--;//区间f1[sum−x,sum-1]+=1
j += len;
sum -= len+1;
}else if(a[j]==x){ //更新答案
f1[sum]+=f2[sum];//对差分数组求前缀和并更新到f1中去
f2[sum+1]+=f2[sum];
f2[sum]=0;
f1[sum]++;//当前前缀和的个数++
res += f1[sum];//累加为∑[0,sum−1]f(i)
sum++;
ans += res;
}else{
f1[sum]++; sum--;
res -= f1[sum];
ans += res;
}
minn = min(minn, sum);
}
}
cout<<ans<<"\n";
for(auto &i: s)v[i].clear();
}
return 0;
}