第二届全国大学生算法设计与编程挑战赛(春季赛)D - zeal(莫队算法)

Description

Yassin 最近在量化投资方面很有兴趣。

为了研究哪只股票是真正的牛股,他把历史 nn 天每一天成交量最大的股票代码写成了一排,并构建了一套属于自己的“理论体系”。

成交量多说明人气好,人气好的肯定买的人多,赚钱就要靠人气! – Yassin

但是知道的人太多,这个大家都去接盘,那就都成为韭菜了 – Makik

基于这个理论,Yassin 想知道 [L, R] 区间中人气“比较”好的股票有哪些,具体而言,他会给定你 L, R, k,你则需要告诉他 [L, R] 中出现 k 次的股票有多少只。

例如 n = 5 时,假设这个代码序列为 {1, 1, 2, 3, 1} 现在他给了一个询问 (2, 5, 1),你就需要回答他 {a2,a3,a4,a5} 中恰好出现 1 次的有多少种元素。答案显然为 2,恰好出现一次的元素为 2,3。

数据保证 ai > 0, k <= n <= 4e4, q <= 4e4, l <= r。

Input
第一行 2 个数字 n, q 分别表示序列的长度和询问的个数。
接下来一行 n 个数字,为序列 {an}
接下来 q 行,每行三个数字 L, R, k, 如题目描述所述。

Output
共 q 行,每行一个数字,为对应询问的答案。

Example

5 1
1 1 2 3 1
2 5 1

2

Hint

本题目描述中所涉及到的投资知识仅作为娱乐,不作为投资建议。
股市有风险,入市需谨慎。

题目大意:

输入n,q。 表示一共有n 个数,q次询问,接下来输入n个数字,a1 … an, 对于每个询问,输入L, R, K,输出[L, R]区间内恰好出现K次的数的个数是多少。

解题思路:

题意很明显是一道数据结构题,区间查询,并且不修改只询问,对于这类区间问题,此时可以使用莫队算法,对于每个询问分快排序,离线后处理每个询问即可。复杂度O(n√n)
莫队算法维护两个数组,一个是cnt,cnt[x] 表示x出现了几次,res[x] 表示恰好出现了x次的数量,然后每个询问双指针移动add or del即可。

Code:

#include 

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 1e5 +50;

int n, m;
int a[N], belong[N], ans[N], cnt[N], res[N];
struct node {
     
    int l, r, k, id;
} q[N];

bool cmp(node a, node b) {
     
	return belong[a.l] ^ belong[b.l] ? belong[a.l] < belong[b.l] : ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r);
}

void del(int x) {
     
    res[cnt[x]] --;
    cnt[x] --;
    res[cnt[x]] ++;
}

void add(int x) {
     
    res[cnt[x]] --;
    cnt[x] ++;
    res[cnt[x]] ++;
}

int main() {
     
	ios::sync_with_stdio(false);
	cin >> n >> m;
	double size = sqrt(n);
	int num = ceil(n / size);
	for (int i = 1; i <= num; i ++)
		for (int j = (i - 1) * size + 1; j <= i * size; j ++)
			belong[j] = i;
	for (int i = 1; i <= n; i ++) cin >> a[i];
	
	for  (int i = 1; i <= m; i ++) {
     
		cin >> q[i].l >> q[i].r >> q[i].k;
		q[i].id = i;
	}
	
	sort(q + 1, q + 1 + m, cmp);
	int l = 1, r = 0;
	for (int i = 1; i <= m; i ++) {
     
		int ql = q[i].l, qr = q[i].r, k = q[i].k;
		while (l < ql) del(a[l ++]);
		while (l > ql) add(a[-- l]);
		while (r < qr) add(a[++ r]);
		while (r > qr) del(a[r --]);
		ans[q[i].id] = res[k];
	}
	
	for (int i = 1; i <= m; i ++) cout << ans[i] << endl;
	
	return 0;
}

你可能感兴趣的:(莫队算法)