【2021杭电多校赛】2021“MINIEYE杯”中国大学生算法设计超级联赛(5)

目录

1006 Cute Tree (模拟)

1009 Array


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();

	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值