杭电多校J 莫队

距离上次写莫队已经过去半年了,莫队是什么东西都快忘了,而今天杭电多校的J题考了莫队的运用。所以,裂开。

回到正文。莫队在我看来是是一个通过分块和排序,使得在离线询问的前提下,能过减少很多不必要运算的过程。

分块的大小一般来说是sqrt(n),这题是数据范围是1e5,所以 k 取300左右。

聊回此题,我们要先对横坐标分块排序,这样就可以使得询问的区间在一个小范围内波动。

struct AC
{
	int x1, y1, x2, y2, idx;
	bool friend operator<(AC m1, AC m2) {
		if (m1.x1 / k != m2.x1 / k)return m1.x1 / k < m2.x1 / k;
		else {
			if((m1.x1/k)&1)return m1.x2  < m2.x2 ;
			else return m1.x2 > m2.x2;

		}
	}
}p[MAXN];

这里注意一个小细节,如果当前分块是奇数块,就正着排,否则就反正排,这种玄学优化可以加速100ms左右。

计算答案的方式是对纵坐标分块

	function<void(int)>add = [&](int t) {
			v[t]++;
			if (v[t] == 1)sum[t / k]++;
		};
		function<void(int)>dec = [&](int t) {
			v[t]--;
			if (v[t] == 0)sum[t / k]--;
		};

sum数组的使用是一种类似前缀和的思想。

function<int(int)>cal = [&](int t) {
			int ans = 0;
			for (int i = 0; i < t / k; i++) {
				ans += sum[i];
			}
			for (int i = (t / k)* k; i <= t; i++) {
				ans += (v[i] > 0);
			}
			return ans;
		};

这里要足以i<t/k,至于为什么不是等于,可以手动画一画,这个点很难发现,我一开始是取等的,所以一致WA。

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
#define k 303
struct AC
{
	int x1, y1, x2, y2, idx;
	bool friend operator<(AC m1, AC m2) {
		if (m1.x1 / k != m2.x1 / k)return m1.x1 / k < m2.x1 / k;
		else {
			if((m1.x1/k)&1)return m1.x2  < m2.x2 ;
			else return m1.x2 > m2.x2;

		}
	}
}p[MAXN];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin >> T;
	while (T--) {
		int n, m;
		cin >> n >> m;
		vector<int>s(n + 1);
		for (int i = 1; i <= n; i++) cin >> s[i];
		vector<int>sum(n + 1), v(n + 1);
		for (int i = 1; i <= m; i++) {
			cin >> p[i].x1 >> p[i].y1 >> p[i].x2 >> p[i].y2;
			p[i].idx = i;
		}
		sort(p + 1, p + 1 + m);
		int l = p[1].x1, r = p[1].x2;
		function<void(int)>add = [&](int t) {
			v[t]++;
			if (v[t] == 1)sum[t / k]++;
		};
		function<void(int)>dec = [&](int t) {
			v[t]--;
			if (v[t] == 0)sum[t / k]--;
		};
		for (int i = l; i <= r; i++) {
			add(s[i]);
		}
		vector<int>ans(m + 1);
		function<int(int)>cal = [&](int t) {
			int ans = 0;
			for (int i = 0; i < t / k; i++) {
				ans += sum[i];
			}
			for (int i = (t / k)* k; i <= t; i++) {
				ans += (v[i] > 0);
			}
			return ans;
		};
		ans[p[1].idx] = cal(p[1].y2) - cal(p[1].y1 - 1);
		for (int i = 2; i <= m; i++) {
			while (l > p[i].x1) {
				l--;
				add(s[l]);
			}
			while (r < p[i].x2) {
				r++;
				add(s[r]);
			}
			while (l < p[i].x1) {
				dec(s[l]);
				l++;
			}
			while (r > p[i].x2) {
				dec(s[r]);
				r--;
			}
			ans[p[i].idx] = cal(p[i].y2) - cal(p[i].y1 - 1);
		}
		for (int i = 1; i <= m; i++) {
			cout << ans[i] << endl;
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值