重构算法大赛 D zeal

重构算法大赛 D zeal

给出n个数,q次询问,每次询问求区间(l, r)当中出现k次的数有多少个
0 < a, k <= n <= 4e4, q <= 1e4, l <= r

一个莫队的板子题,通过这道题学到了

对于可以离线的询问,且可以 O ( 1 ) O(1) O(1)时间从ans(l, r)转移到ans(l - 1, r), ans(l + 1, r), ans(l, r + 1)和ans(l, r - 1),就可以用莫队算法

具体的算法流程大致是分块排序,然后暴力区间转移,可以证明复杂度为 O ( n n ) O(n\sqrt{n}) O(nn )

此题当中,用一个cnt数组记录当前出现i次的数有多少个,再用num数组记录i出现的个数,便可以实现 O ( 1 ) O(1) O(1)转移

#include <bits/stdc++.h>
using namespace std;

//faster_reader
inline int read ()
{
	int f = 1, a = 0;
	char c = getchar ();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar ();
	}
	while (c >= '0' && c <= '9')
	{
		a = a * 10 + c - '0';
		c = getchar ();
	}
	return f * a;
}
	
const int N = 4e4 + 10;
int a[N], belong[N], ans[N];
int num[N], cnt[N];

struct query
{
	int l, r, k, num;
};
query q[N];

bool cmp(const query &a, const query &b)
{
	if (belong[a.l] ^ belong[b.l])
		return belong[a.l] < belong[b.l];
	if (belong[a.l] & 1)
		return belong[a.r] < belong[b.r];
	return belong[a.r] > belong[b.r];
}

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

void sub(int x)
{
	cnt[num[a[x]]] --;
	num[a[x]] --;
	cnt[num[a[x]]] ++;
}

int main()
{
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
	
	//input
	int n, m;
	n = read(); m = read();
	for (int i = 1; i <= n; i ++)
		a[i] = read();
	for (int i = 1; i <= m; i ++)
	{
		q[i].l = read();
		q[i].r = read();
		q[i].k = read();
		q[i].num = i;
	}

	int size = sqrt(n);
	for (int i = 1; i <= n; i ++)
		belong[i] = (i - 1) / size + 1;

	sort (q + 1, q + 1 + m, cmp);

	int l = 1, r = 0;
	for (int i = 1; i <= m; i ++)
	{
		int ll = q[i].l, rr = q[i].r;
		while (l > ll) add(-- l);
		while (r < rr) add(++ r);
		while (l < ll) sub(l ++);
		while (r > rr) sub(r --);
		ans[q[i].num] = cnt[q[i].k];
	}

	for (int i = 1; i <= m; i ++)
		printf ("%d\n", ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值