[HDU6959]zoto

Posted by Dispwnl on July 21, 2021

题目

题解

考试的时候想到了莫队套数据结构维护,但是没想到分块……

首先可以把题目简化成二维矩阵的查询,如果去掉查询中$y$坐标的限制,即查询一段区间内不同数的个数,这个显然是可以用莫队搞的;而对于加上$y$的限制,考虑能否用数据结构维护莫队每一步移动对于$y$值域的贡献以及查询

如果用树状数组等数据结构维护,可以做到$O(logM)$的修改和查询,但是这样复杂度就为$O((n\sqrt n + m)logM)$,发现修改操作的复杂度过高,考虑分块,对于$y$值域分块,每次莫队移动相当于对于值域中的某个点$+1$或$-1$,查询的时候直接查询区间内不为$0$的点个数,这样总复杂度就变为$O(n\sqrt n+m\sqrt M)$,其中$M$为最大值域

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAX = 1e5 + 5, N = 320;
int n, m, K, M;
int f[MAX], pos[MAX], pox[MAX], val[MAX], Ans[MAX];
int pL[N], pR[N], num[N];
struct p{
	int l, r, ly, ry, id;
	bool operator< (const p &a)
	const {
		if(pos[l] != pos[a.l])
			return pos[l] < pos[a.l];
		return r < a.r;
	}
}q[MAX];

int read()
{
	int x = 0;
	char ch = getchar();
	for(; !isdigit(ch); ch = getchar());
	for(; isdigit(ch); x = x * 10 + ch - '0', ch = getchar());
	return x;
}

void Mem()
{
	memset(num, 0, sizeof(num));
	memset(val, 0, sizeof(val));
	memset(pL, 0, sizeof(pL));
	memset(pR, 0, sizeof(pR));
}

void Upt(int x, int v)
{
	int px = pox[x];
	val[x] += v;
	if(!val[x] && v == -1)
		-- num[px];
	else if(val[x] == 1 && v == 1)
		++ num[px];
}

int Ask(int l, int r)
{
	int pl = pox[l], pr = pox[r];
	int ans = 0;
	for(int i = pl + 1; i <= pr - 1; ++ i)
		ans += num[i];
	for(int i = l; i <= min(pR[pl], r); ++ i)
		ans += val[i] ? 1 : 0;
	if(pl != pr)
		for(int i = pL[pr]; i <= r; ++ i)
			ans += val[i] ? 1 : 0;
	return ans;
}

int main()
{
	int T = read();
	while(T --)
	{
		Mem();
		n = read(), m = read(), K = sqrt(n);
		for(int i = 1; i <= n; ++ i)
			M = max(M, f[i] = read() + 1), pos[i] = (i - 1) / K + 1;
		for(int i = 1; i <= m; ++ i)
		{
			q[i].l = read(), q[i].ly = read() + 1;
			q[i].r = read(), q[i].ry = read() + 1;
			q[i].id = i;
			M = max(M, q[i].ry);
		}
		sort(q + 1, q + 1 + m);
		int Km = sqrt(M);
		for(int i = 1; i <= M; ++ i)
		{
			pox[i] = (i - 1) / Km + 1;
			if(!pL[pox[i]])
				pL[pox[i]] = i;
			pR[pox[i]] = i;
		}
		for(int i = 1, l = 1, r = 0; i <= m; ++ i)
		{
			while(l < q[i].l)
				Upt(f[l ++], -1);
			while(l > q[i].l)
				Upt(f[-- l], 1);
			while(r < q[i].r)
				Upt(f[++ r], 1);
			while(r > q[i].r)
				Upt(f[r --], -1);
			Ans[q[i].id] = Ask(q[i].ly, q[i].ry);
		}
		for(int i = 1; i <= m; ++ i)
			printf("%d\n", Ans[i]);
	}
	return 0;
}