莫队

普通莫队

SP3267 DQUERY - D-query

题目

(mathcal{Description})

给出一个长度为 (n) 的数列,有 (q) 个询问,需要求出在 ((l, r)) 区间内有多少不同的数

(mathcal{Solution})

这题就是 [SDOI2009]HH的项链 的数据弱化版,可以把他当做莫队的模板题。

考虑最简单的做法,也就是暴力,用一个 (cnt) 数组记录 ([l, r]) 区间内(暴力扫一遍)每个数出现的次数,再扫一遍 (cnt) 数组统计不为 (0) 的个数,输出答案。

很容易想出第一步优化,在记录 (cnt) 数组的同时,在加之前判断下是否为 (0),也就是之前有没有出现过这个数,如果为 (0) 那么(ans++)

再考虑下优化,我们可以弄两个指针 (l)(r) ,每次询问移动 (l)(r) 到询问的区间,统计 (ans) 时只需在两个指针处加减 (cnt)

当然如果他需要查找的区间是这样的

所以我们还需将查询区间的左端点进行排序。

(mathcal{Code})

#include<bits/stdc++.h>
using namespace std;
const int N = 3e4 + 10, M = 2e6 + 10;
int n, m, now, be[M], a[M], cnt[M], ans[M];
struct Node {
	int l, r, id;
} q[M];
inline int read() {
	int x = 0, k = 1; char c = getchar();
	for (; c < 48 || c > 57; c = getchar()) k ^= (c == '-');
	for (; c >= 48 && c <= 57; c = getchar()) x = x * 10 + (c ^ 48);
	return k ? x : -x;
}
inline bool cmp(Node a, Node b) {  // 奇偶性排序
	return (be[a.l] ^ be[b.l]) ? be[a.l] < be[b.l] : ((be[a.l] & 1) ? a.r < b.r : a.r > b.r);
} 
int main() {
	n = read();
	int sz = sqrt(n), n1 = ceil((double)n / sz);
	for (int i = 1; i <= n1; i++)   // 分块
		for (int j = (i - 1) * sz + 1; j <= i * sz; j++)
			be[j] = i;
	for (int i = 1; i <= n; i++)
		a[i] = read();
	m = read();
	for (int i = 1; i <= m; i++)
		q[i].l = read(), q[i].r = read(), q[i].id = i;
	std::sort(q + 1, q + 1 + m, cmp);
	int L = 1, R = 0;
	for (int i = 1; i <= m; i++) {
		int l = q[i].l, r = q[i].r;
		while (L < l)
			now -= !--cnt[a[L++]];
		while (L > l)
			now += !cnt[a[--L]]++;
		while (R < r)
			now += !cnt[a[++R]]++;
		while (R > r)
			now -= !--cnt[a[R--]];
		ans[q[i].id] = now;
	}
	for (int i = 1; i <= m; i++)
		printf("%d
", ans[i]);
	return 0;
}

[国家集训队]小Z的袜子

题目

(mathcal{Description})

给出一个长度为 (n) 的数列,有 (q) 个询问,需要求出在 ([l, r]) 区间内随机取出两个数数字一样的概率,若该概率为 (0) 则输出 (0/1) ,否则输出的 (A/B) 必须为最简分数。如果 (l == r) 则输出 (0 / 1)

(mathcal{Solution})

基本与上面一题一样。

取到两个相同的数的方案数为 (frac{sum _ {i = 1} ^ {N} (cnt[i] - 1) * cnt[i]}{2} (cnt[i] geq 2))

总方案数为 (frac {(r - l + 1) * (r - l)}{2})

(mathcal{Code})

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10, M = 5e6 + 10;
int n, m, now, be[M], a[M], cnt[N], ansx[M], ansy[M];
struct Node {
	int l, r, id;
} q[M];
inline  int read() {
	register int x = 0, k = 1; char c = getchar();
	for (; c < 48 || c > 57; c = getchar()) k ^= (c == '-');
	for (; c >= 48 && c <= 57; c = getchar()) x = x * 10 + (c ^ 48);
	return k ? x : -x;
}
inline void print(int x) {
    if(x / 10) print(x / 10);
    putchar(x % 10 + '0');
}
inline bool cmp(Node a, Node b) {
	return (be[a.l] != be[b.l]) ? be[a.l] < be[b.l] : ((be[a.l] & 1) ? a.r < b.r : a.r > b.r);
} 
signed main() {
	int n = read();
	m = read();
	register int sz = sqrt(n);
	for (register int i = 1; i <= n; i++)
		a[i] = read(), be[i] = i / sz + 1;
	for (register int i = 1; i <= m; i++)
		q[i].l = read(), q[i].r = read(), q[i].id = i;
	std::sort(q + 1, q + 1 + m, cmp);
	register int L = 1, R = 0, l, r;
	for (register int i = 1; i <= m; i++) {
		l = q[i].l, r = q[i].r;
		if (l == r) {
			ansx[q[i].id] = 0, ansy[q[i].id] = 1;
			continue;
		}
		while (L > l) {
			L--;
			now -= cnt[a[L]] * (cnt[a[L]] - 1);
			cnt[a[L]]++;
			now += cnt[a[L]] * (cnt[a[L]] - 1);
		}
		while (L < l) {
			now -= cnt[a[L]] * (cnt[a[L]] - 1);
			cnt[a[L]]--;
			now += cnt[a[L]] * (cnt[a[L]] - 1);
			L++;
		}
		while (R > r) {
			now -= cnt[a[R]] * (cnt[a[R]] - 1);
			cnt[a[R]]--;
			now += cnt[a[R]] * (cnt[a[R]] - 1);
			R--;
		}
		while (R < r) {
			R++;
			now -= cnt[a[R]] * (cnt[a[R]] - 1);
			cnt[a[R]]++;
			now += cnt[a[R]] * (cnt[a[R]] - 1);
		}
		ansx[q[i].id] = now;
		ansy[q[i].id] = (q[i].r - q[i].l + 1) * (q[i].r - q[i].l);
	}
	for (register int i = 1; i <= m; i++) {
		if (!ansx[i]) {
			printf("0/1
");
			continue;
		}
		int d = __gcd(ansx[i], ansy[i]);
		printf("%lld/%lld
", ansx[i] / d, ansy[i] / d);
	}
	return 0;
}

小清新人渣的本愿

题目

(mathcal{Description})

给出一个长度为 (n) 的数列,有 (q) 个询问,每次询问给出 (opt)(l)(r)(x)

1、若 (opt)(1), 询问一个区间是否可以选出两个数差为 (x)

2、若 (opt)(2), 询问一个区间是否可以选出两个数和为 (x)

1、若 (opt)(3), 询问一个区间是否可以选出两个数乘积为 (x)

对于每个询问,如果可以,输出hana,如果不可以,输出bi

(mathcal{Solution})

如果想学 (bitset) 可以进这个链接

当然本题只需要知道一些很基础的。

(bitset) 维护,每一位表示每个数字是否存在,记为(now1)

(opt == 1),如果存在 (z - y = x), 则必存在 (z)(z - x)

(opt == 2),就是求 (z + y = x),所以除 (now1) 外,我们还需用 (bitset) 记录一个 (now1) 的反串,就是每一位 (i) 都表示 (N - i)。问题就转化成满足 (now1) 中有 (z)(now2) 中有 (y^{'})。代入 (z + y = x) 中,得到 (z + N - y ^ {'} = x)(z - y ^ {'} = x - N)

(opt == 3),直接枚举约数。

(mathcal{Code})

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 2e6 + 10;
int n, m, now, be[M], a[M], cnt[M], ans[M];
bitset <N + 10> now1, now2;
struct Node {
	int l, r, id, k, x;
} q[M];
inline int read() {
	int x = 0, k = 1; char c = getchar();
	for (; c < 48 || c > 57; c = getchar()) k ^= (c == '-');
	for (; c >= 48 && c <= 57; c = getchar()) x = x * 10 + (c ^ 48);
	return k ? x : -x;
}
inline bool cmp(Node a, Node b) {
	return (be[a.l] ^ be[b.l]) ? be[a.l] < be[b.l] : ((be[a.l] & 1) ? a.r < b.r : a.r > b.r);
} 
inline void del(int x) {
	if (--cnt[x] == 0)
		now1[x] = now2[N - x] = 0;
}
inline void add(int x) {
	if (cnt[x]++ == 0)
		now1[x] = now2[N - x] = 1;
}
int main() {
	n = read(), m = read();
	int sz = sqrt(n), n1 = ceil((double)n / sz);
	for (int i = 1; i <= n1; i++)
		for (int j = (i - 1) * sz + 1; j <= i * sz; j++)
			be[j] = i;
	for (int i = 1; i <= n; i++)
		a[i] = read();
	for (int i = 1; i <= m; i++)
		q[i].k = read(), q[i].l = read(), q[i].r = read(), q[i].x = read(), q[i].id = i;
	std::sort(q + 1, q + 1 + m, cmp);
	int L = 1, R = 0;
	for (int i = 1; i <= m; i++) {
		int l = q[i].l, r = q[i].r, k = q[i].k, x = q[i].x;
		while (L < l)
			del(a[L++]);
		while (L > l)
			add(a[--L]);
		while (R < r)
			add(a[++R]);
		while (R > r)
			del(a[R--]);
		if (k == 1) {
			if ((now1 & (now1 << x)).any()) {
				ans[q[i].id] = 1;
				continue;
			}
		}
		if (k == 2) {
			if ((now1 & (now2 >> (N - x))).any()) {
				ans[q[i].id] = 1;
				continue;
			}
		}
		if (k == 3) {
			for (int j = 1; j * j <= x; j++)
				if ((!(x % j)) && (now1[j] && now1[x / j])) {
					ans[q[i].id] = 1;
					continue;
				}
		}
	}
	for (int i = 1; i <= m; i++)
		if (!ans[i])
			puts("bi");
		else 
			puts("hana");
	return 0;
}

带修莫队

[国家集训队]数颜色 / 维护队列

题目

(mathcal{Description})

给出一个长度为 (n) 的数列,有 (q) 个操作。

1、(Q) (l) (r) 代表询问 ([l, r]) 有多少不同的数。

2、(C) (P) (Col) 代表把第 (P) 个数变成 (Col)

(mathcal{Solution})

对于带修莫队,实际上就是在原来普通莫队上加上一个时间戳。于是当前区间移动方向就由四个([l - 1, r])([l + 1, r])([l, r - 1])([l, r + 1])变为六个([l - 1, r, t])([l + 1, r, t])([l, r - 1, t])([l, r + 1, t])([l, r, t - 1])([l, r, t + 1])

(mathcal{Code})

#include<bits/stdc++.h>
using namespace std;
const int N = 3e4 + 10, M = 2e6 + 10;
int n, m, now, be[M], a[M], cnt[M], ans[M], cntc, cntq;
char ch;
struct Query {
	int l, r, id, time;
} q[M];
struct Replace {
	int pos, color;
} c[M];
inline int read() {
	int x = 0, k = 1; char c = getchar();
	for (; c < 48 || c > 57; c = getchar()) k ^= (c == '-');
	for (; c >= 48 && c <= 57; c = getchar()) x = x * 10 + (c ^ 48);
	return k ? x : -x;
}
inline bool cmp(Query a, Query b) {
	return (be[a.l] ^ be[b.l]) ? be[a.l] < be[b.l] : ((be[a.r] ^ be[b.r]) ? be[a.r] < be[b.r] : a.time < b.time);
} 
int main() {
	n = read(), m = read();
	int sz = pow(n, 2.0 / 3.0), n1 = ceil((double)n / sz);
	for (int i = 1; i <= n1; i++)
		for (int j = (i - 1) * sz + 1; j <= i * sz; j++)
			be[j] = i;
	for (int i = 1; i <= n; i++)
		a[i] = read();
	for (int i = 1; i <= m; i++) {
		scanf("%c", &ch);
		while (ch != 'Q' && ch != 'R')
			scanf("%c", &ch);
		if (ch == 'Q') {
			q[++cntq].l = read(), q[cntq].r = read(), q[cntq].time = cntc, q[cntq].id = cntq;
		}
		else if (ch == 'R') {
			c[++cntc].pos = read(), c[cntc].color = read();
		}
	}
	std::sort(q + 1, q + 1 + cntq, cmp);
	int L = 1, R = 0, Time = 0;
	for (int i = 1; i <= m; i++) {
		int l = q[i].l, r = q[i].r, time = q[i].time;
		while (L < l)
			now -= !--cnt[a[L++]];
		while (L > l)
			now += !cnt[a[--L]]++;
		while (R < r)
			now += !cnt[a[++R]]++;
		while (R > r)
			now -= !--cnt[a[R--]];
		while (Time < time) {
			Time++;
			if (l <= c[Time].pos && r >= c[Time].pos)
				now -= !--cnt[a[c[Time].pos]] - !cnt[c[Time].color]++;
			std::swap(a[c[Time].pos], c[Time].color);
		}
		while (Time > time) {
			if (l <= c[Time].pos && r >= c[Time].pos)
				now -= !--cnt[a[c[Time].pos]] - !cnt[c[Time].color]++;
			std::swap(a[c[Time].pos], c[Time].color);
			Time--;
		}
		ans[q[i].id] = now;
	}
	for (int i = 1; i <= cntq; i++)
		printf("%d
", ans[i]);
	return 0;
}

树上莫队、回滚莫队

原文地址:https://www.cnblogs.com/wjnclln/p/11601623.html