学佳澳杯 F 区间计数

双指针、尺取

题目

alt

样例及输出

4 2
1 3 2 4
3 2
2 4
5

思路

from 神秘的学长

双指针, 尺取

枚举每一个i可以到达的最远的j, 使得<i, j>内, 每一个人的敌人数量为0, 我们用 emy[] 记录

1、一段区间是合法的, 当且仅当区间内所有的人的所有敌人, 在此区间内出现次数为0 即 我们只要保证每次右端点加入的点, 自身的敌人不在区间内, 我们用emy记录i敌人的个数 while (j <= n && !emy[a[j]]) j ++ ;

2、每加入一个点a[j], 我们都要更新一下它的敌人A, 标记e[A] ++ , 表示A的敌人至少存在一个 for (auto it : h[a[j]]) emy[it] ++ ;

3、在移动先统计一下 ans, 因为j退出的时候是非法状态, 故合法长度为 [i, j-1], 即 ans += (j-1-i+1)

4、i每次都前进一步, 所以我们要用a[i]更新它的敌人, 使得数量减一

注意 1e6 不用考虑离散化, 这里时限卡掉 unordered_map

因为指针i、j一直往后移动, 所以时间复杂度为 O(n)

人畜无害的代码

#include <iostream>
#include <unordered_map>
#include <vector>
#define int long long

using namespace std;

const int N = 1e6 + 12;

int n, m;
int a[N];
int emy[N]; // emy[i] 动态维护区间内i的敌人数量 
vector<int> h[N];

signed main()
{
	cin.tie(0) -> sync_with_stdio(0);
	
	cin >> n >> m;
	for (int i=1;i <= n;i ++ ) cin >> a[i], emy[a[i]] = 0;
	for (int i=1;i <= m;i ++ )
	{
		int u, v; cin >> u >> v;
		h[u].push_back(v);
		h[v].push_back(u);	
	}
	
	int ans = 0;	
	for (int i=1, j=1;i <= n;i ++ )
	{
		while (j <= n && !emy[a[j]])
		{
			for (auto it:h[a[j]]) emy[it] ++ ;
			j ++ ;	
		}
		
		ans += j - i;
		
		for (auto it:h[a[i]]) emy[it] -- ;
	}
	
	cout << ans;
	
	return 0;
}