P4396 [AHOI2013]作业 (莫队 + 值域分块)

题目链接: P4396 [AHOI2013]作业

大致题意

给定一个长度为 n n n的序列, 有 m m m次询问, 每次询问给出l r a b.

表示询问在区间 [ l , r ] [l, r] [l,r]内, 值域 [ a , b ] [a, b] [a,b]之间有多少个数值, 以及有多少个不同数值.

解题思路

莫队 + 值域分块

我们通过莫队做到 O ( n ) O(\sqrt{n}) O(n )维护 [ l , r ] [l, r] [l,r]的所有数值信息, 在查询时, 通过分块来做到 O ( n ) O(\sqrt{n}) O(n )的查询.

➡️同类题目推荐⬅️

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10, B = 320;
int w[N];
struct mo {
	int l, r, a, b, id;
	bool operator< (const mo& t) const {
		if (l / B != t.l / B) return l < t.l;
		return l / B & 1 ? r < t.r : r > t.r;
	}
}; vector<mo> area;


int num[N], bcou[B], bhave[B];
pair<int, int> res[N];
void add(int c) {
	num[c]++, bcou[c / B]++;
	if (num[c] == 1) bhave[c / B]++;
}
void sub(int c) {
	num[c]--, bcou[c / B]--;
	if (!num[c]) bhave[c / B]--;
}
pair<int, int> ask(int a, int b) {
	int cou1 = 0, cou2 = 0;
	if (a / B == b / B) { // 同块
		while (a <= b) cou1 += num[a], cou2 += num[a++] != 0;
		return { cou1, cou2 };
	}

	int ba = a / B, bb = b / B;
	while (a / B == ba) cou1 += num[a], cou2 += num[a++] != 0;
	while (b / B == bb) cou1 += num[b], cou2 += num[b--] != 0;

	for (int i = a / B; i <= b / B; ++i) {
		cou1 += bcou[i], cou2 += bhave[i];
	}

	return { cou1, cou2 };
}
int main()
{
	int n, m; cin >> n >> m;
	rep(i, n) scanf("%d", &w[i]);
	rep(i, m) {
		int l, r, a, b;
		scanf("%d %d %d %d", &l, &r, &a, &b);
		area.push_back({ l, r, a, b, i });
	}
	sort(area.begin(), area.end());

	int L = 1, R = 0;
	for (auto& [l, r, a, b, id] : area) {
		while (l < L) add(w[--L]);
		while (r > R) add(w[++R]);
		while (L < l) sub(w[L++]);
		while (R > r) sub(w[R--]);

		res[id] = ask(a, b);
	}

	rep(i, m) printf("%d %d\n", res[i].first, res[i].second);

	return 0;
}

END

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值