「洛谷1903」「BZOJ2120」「国家集训队」数颜色【带修莫队,树套树】

题目链接

【BZOJ传送门】
【洛谷传送门】

题目大意

单点修改,区间查询有多少种数字。

解法1–树套树

可以直接暴力树套树,我比较懒,不想写。
稍微口胡一下,可以直接来一个树状数组套主席树,也就是待修的主席树。
然后查询的时候,两个根节点减一下就可以了。

解法2–带修莫队

这是带修莫队的模板题。
最简单的莫队是是一个二元组 ( l , r ) (l,r) (l,r),这里引入了一个新的参数,变成了三元组 ( l , r , t ) (l,r,t) (l,r,t) t t t所表示的是在这个查询最前面的哪一个修改的编号。
然后我们这个 t t t当做第三关键字,进行询问的排序。

bool cmp(const Que_rec &A, const Que_rec &B) { return A.l / block == B.l / block ? (A.r / block == B.r / block ? A.t < B.t : A.r < B.r) : A.l < B.l; }

也就是把 r r r也进行块的排序。
那么在查询的最后,我们需要加上修改操作。
因为如果一个东西修改过后,但是我们先查询了,那么就把这个东西变回去,然后再修改就好了。

代码

开了 O ( 2 ) O(2) O(2)才过掉的垃圾代码。

#include <bits/stdc++.h>
#pragma GCC optimize
#define N 50105
#define M 1000005
using namespace std;
template <typename T> void read(T &x) {
    x = 0; T fl = 1; char ch = 0;
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') fl = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
    x *= fl;
}
struct Que_rec { int l, r, t, id; } q[N];
struct Moy_rec { int id, val; } mo[N];
int block, n, m, q_cnt = 0, m_cnt = 0, res; 
int a[N], ddd[M], ans[N];
bool cmp(const Que_rec &A, const Que_rec &B) { return A.l / block == B.l / block ? (A.r / block == B.r / block ? A.t < B.t : A.r < B.r) : A.l < B.l; }
void upda(int x) { if (ddd[x] ++ == 0) res ++; }
void updd(int x) { if (-- ddd[x] == 0) res --; }
void modify(int lmo, int id) {
	if (mo[lmo].id >= q[id].l && mo[lmo].id <= q[id].r) updd(a[mo[lmo].id]), upda(mo[lmo].val);
	swap(a[mo[lmo].id], mo[lmo].val);
}
int main() {
	read(n); read(m); block = (int)sqrt(1.0 * n + 0.5) * 2;
	for (int i = 1; i <= n; i ++) read(a[i]);
	for (int i = 1; i <= m; i ++) {
		char opt[5]; scanf("%s", opt); int x, y;
		if (opt[0] == 'Q') { read(x); read(y); ++ q_cnt; q[q_cnt] = (Que_rec){x, y, m_cnt, q_cnt}; }
		else { m_cnt ++; read(x); read(y); mo[m_cnt] = (Moy_rec){x, y}; }
	}
	sort(q + 1, q + 1 + q_cnt, cmp);
	int l = 2, r = 1, lmo = 0; res = 0;
	for (int i = 1; i <= q_cnt; i ++) {
		while (r < q[i].r) upda(a[++ r]);
		while (r > q[i].r) updd(a[r --]);
		while (l > q[i].l) upda(a[-- l]);
		while (l < q[i].l) updd(a[l ++]);
		while (lmo < q[i].t) modify(++ lmo, i);
		while (lmo > q[i].t) modify(lmo --, i);
		ans[q[i].id] = res; 
	}
	for (int i = 1; i <= q_cnt; i ++) printf("%d\n", ans[i]);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值