目录
1006 Cute Tree (模拟)
之后补
1009 Array
相关题目:主要元素问题
这道相关问题要求空间复杂度为O(1)时间复杂度为O(n),因此用到了Boyer-Moore 投票算法
Boyer-Moore 投票算法的步骤如下:
1.维护一个候选主要元素 candidate 和候选主要元素的出现次数 count,初始时 candidate 为任意值,count=0;
2.遍历数组中的所有元素,遍历到下一个元素X时,进行如下操作:
(1)如果count=0,则将X的值赋给candidate,否则保持candidate 的值;
(2)如果x=candidate,count++,否则count--
3.遍历结束后,若数组中存在主要元素,那么candidate即为主要元素,否则candidate可以为数组中任意一个元素
最后还要再遍历一遍数组,看candidate出现次数是否大于数组长度的一半(因为数组中不一定存在主要元素,投票算法只保证若存在主要元素最后剩下的一定是主要元素)
本题题意
当元素的出现的次数大于区间长度的一半时则该数为众数,求有多少个有众数的区间。
本题题解
枚举每一个可能出现的众数,从左到右枚举右端点
众数的位置为1,不是为-1,当区间和大于0则符合。求sum前缀和,对于一个sumr,如果左侧有suml小于sumr则存在一种情况。用f1存储sum为i有几种情况,f2为f1的差分数组,用于维护值为-1的线段
出现三种情况
(1)a[j]==x 此时更新答案
(2)a[j]!=x 但是此时的sum>minn说明还可以把答案加进去
(3)a[j]!=x 而且此时的sum==minn,说明之后的-1得出的sum都比min小不用更新答案
最后更新min的值
#include<iostream>
#include<vector>
#include <set>
#include<map>
#include<algorithm>
using namespace std;
#define maxn 2000005
vector<int> v[maxn];//记录每个数字出现的位置
int main()
{
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
vector<int> a(n + 1);
set<int>s;//记录出现的数字种类(用集合防止重复)
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
v[a[i]].push_back(i);
s.insert(a[i]);
}
long long ans=0;
for (int x : s)//枚举每个出现过的数字
{
long long res = 0;//x这个数对答案的贡献
long long sum = 0;//当前前缀和
map<int, int> f1, f2;//记录前缀和为sum的有f1个,f2为f1的差分数组
v[x].push_back(n + 1);
long long k = 0;
long long minn = 0;//历史前缀最小值
for (int j = 1; j <= n; j++)
{
if (j > v[x][k]) k++;
if (a[j] == x)
{
f1[sum] += f2[sum];//通过差分数组更新一下f1的值
f2[sum + 1] += f2[sum];
f2[sum] = 0;
f1[sum]++;//当前前缀和个数+1
res += f1[sum];
sum++;
ans += res;
}
else
if (sum > minn)
{
f1[sum]++;
sum--;
res = res - f1[sum];
ans += res;
}
else//sum==minn
{
long long len = v[x][k] - 1 - j;
f2[sum - len]++;
f2[sum + 1]--;//利用f2差分数组使得f1[sum-x,sum-1]+1
j += len;
sum -= len + 1;
}
minn = min(minn, sum);
}
}
cout << ans << "\n";
for (auto& i : s)v[i].clear();
}
}