莫队

普通莫队

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\n", 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\n");
			continue;
		}
		int d = __gcd(ansx[i], ansy[i]);
		printf("%lld/%lld\n", 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\n", ans[i]);
	return 0;
}

树上莫队、回滚莫队

posted @ 2019-09-28 09:48  Trimsteanima  阅读(147)  评论(0编辑  收藏  举报
Live2D