【Codeforces】 CF 2009 G1

Yunli’s Subarray Queries (easy version)

#莫队 #数据结构

题目描述

This is the easy version of the problem. In this version, it is guaranteed that r = l + k − 1 r=l+k-1 r=l+k1 for all queries.

For an arbitrary array b b b, Yunli can perform the following operation any number of times:

  • Select an index i i i. Set b i = x b_i = x bi=x where x x x is any integer she desires ( x x x is not limited to the interval [ 1 , n ] [1,n] [1,n]).

Denote f ( b ) f(b) f(b) as the minimum number of operations she needs to perform until there exists a consecutive subarray ∗ ^{\text{∗}} of length at least k k k in b b b.

Yunli is given an array a a a of size n n n and asks you q q q queries. In each query, you must output ∑ j = l + k − 1 r f ( [ a l , a l + 1 , … , a j ] ) \sum_{j=l+k-1}^{r} f([a_l, a_{l+1}, \ldots, a_j]) j=l+k1rf([al,al+1,,aj]). Note that in this version, you are only required to output f ( [ a l , a l + 1 , … , a l + k − 1 ] ) f([a_l, a_{l+1}, \ldots, a_{l+k-1}]) f([al,al+1,,al+k1]).

∗ ^{\text{∗}} If there exists a consecutive subarray of length k k k that starts at index i i i ( 1 ≤ i ≤ ∣ b ∣ − k + 1 1 \leq i \leq |b|-k+1 1ibk+1), then b j = b j − 1 + 1 b_j = b_{j-1} + 1 bj=bj1+1 for all i ≤ ; j ≤ i + k − 1 i \leq; j \leq i+k-1 i;ji+k1.

输入格式

The first line contains t t t ( 1 ≤ t ≤ 1 0 4 1 \leq t \leq 10^4 1t104) — the number of test cases. The first line of each test case contains three integers n n n, k k k, and q q q ( 1 ≤ k ≤ n ≤ 2 ⋅ 1 0 5 1 \leq k \leq n \leq 2 \cdot 10^5 1kn2105, 1 ≤ q ≤ 2 ⋅ 1 0 5 1 \leq q \leq 2 \cdot 10^5 1q2105) — the length of the array, the length of the consecutive subarray, and the number of queries. The following line contains n n n integers a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an ( 1 ≤ a i ≤ n 1 \leq a_i \leq n 1ain). The following q q q lines contain two integers l l l and r r r ( 1 ≤ l ≤ r ≤ n 1 \leq l \leq r \leq n 1lrn, r = l + k − 1 r=l+k-1 r=l+k1) — the bounds of the query. It is guaranteed that the sum of n n n over all test cases does not exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2105 and the sum of q q q over all test cases does not exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2105.

输出格式

Output ∑ j = l + k − 1 r f ( [ a l , a l + 1 , … , a j ] ) \sum_{j=l+k-1}^{r} f([a_l, a_{l+1}, \ldots, a_j]) j=l+k1rf([al,al+1,,aj]) for each query on a new line.

样例 #1

样例输入 #1

3
7 5 3
1 2 3 2 1 2 3
1 5
2 6
3 7
8 4 2
4 3 1 1 2 4 3 2
3 6
2 5
5 4 2
4 5 1 2 3
1 4
2 5

样例输出 #1

2
3
2
2
2
2
1

解法

解题思路

E a s y Easy Easy版本的窗口大小是固定的,也就是每次给定一个固定的窗口大小和起点,找最小需要被修改的次数,使其内部完全都是连续的 k k k个数。

首先,如何判断窗口内的每个数是否是连续递增的,且相差为 1 1 1呢?

例如以下的数列:
[ 1 , 2 , 5 , 6 , 7 , 8 , 9 ] [1,2,5,6,7,8,9] [1,2,5,6,7,8,9]

可以发现,对于这个窗口 [ 1 , 2 ] [1,2] [1,2]是连续的 [ 5 , 6 , 7 , 8 , 9 ] [5,6,7,8,9] [5,6,7,8,9]也是连续的,它们的值-下标的大小是一样的,比如前者是 0 0 0,后者是 2 2 2。 这样问题就迎刃而解了,我们只需要维护这个窗口中出现最多的那个数的次数,然后用窗口大小减去这个次数,就是要修改的最少次数了。

而对于多个询问,而不存在修改的问题,我们容易想到使用莫队算法来离线处理,当指针移动的时候就使用上述的方法来维护一个存放出现次数的 m u t i s e t mutiset mutiset,每次更新答案即可。

代码

 
int n, m, k, len;
struct node {
	int l, r, id;
	bool operator<(node& x) {
		if ((l - 1) / len + 1 != (x.l - 1) / len + 1) return l < x.l;
		if (((l - 1) / len + 1) & 1) return r < x.r;
		else return r > x.r;
	}
}q[N];

void solve() {
	cin >> n >> k >> m;
 
	len = sqrt(m);
	vector<int>a(n + 1);
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
	}
 
	for (int i = 1; i <= m; ++i) {
		int l, r;
		cin >> l >> r;
		q[i] = { l,r,i };
	}
	
	map<int, int>mp;
	multiset<int>mst;
	sort(q + 1, q + 1 + m);
	
	auto add = [&](int idx)->void {
		mst.extract(mp[a[idx] - idx]);
		mp[a[idx] - idx]++;
		mst.insert(mp[a[idx] - idx]);
		};
 
	auto del = [&](int idx)->void {
		mst.extract(mp[a[idx] - idx]);
		mp[a[idx] - idx]--;
		mst.insert(mp[a[idx] - idx]);
		};
 
	int l = 1, r = 0;
	vector<int>res(m + 1);
	for (int i = 1; i <= m; ++i) {
		while (l > q[i].l) 	add(--l);
		while (l < q[i].l) del(l++);
		while (r < q[i].r) add(++r);
		while (r > q[i].r) del(r--);
 
		res[q[i].id] = k - *mst.rbegin();
		
	}
 
	for (int i = 1; i <= m; ++i) {
		std::cout << res[i] << "\n";
		q[i] = { 0,0,0 };
	}
 
}
signed main() {
	ios::sync_with_stdio(0);
	std::cin.tie(0);
	std::cout.tie(0);
	int t = 1;
	cin >> t;
	while (t--) {
		solve();
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值