大艺术家

题意

给定一个长度为 \(n\) 的颜色序列和 \(m\) 个两两包含或不交的区间。

接下来进行 \(q\) 次单点颜色修改,求每个区间最早满足区间内颜色互不相同的时间。

做法

\(pre_i\) 表示位置 \(i\) 左侧最近的、与 \(c_i\) 颜色相同的位置。

区间 \([l,r]\) 内颜色互不相同,当且仅当:

\[\max_{i=l}^{r} pre_i<l \]

用线段树维护 \(pre_i\) 的区间最大值,每种颜色用一个 set 维护其出现位置。

修改 \(c_x\) 时,只有旧颜色和新颜色中 \(x\) 及其后继的 \(pre\) 会发生改变。

由于所有区间之间只有包含或不交关系,可以建立包含森林。每次修改后,从包含 \(x\) 的最深区间向上检查:

  • 若当前区间仍不合法,则其祖先也不合法,直接停止。
  • 若当前区间合法,则记录答案并继续检查父亲。

使用并查集删除已经得到答案的区间,使之后可以直接跳过这些区间。

初始合法区间的答案为 \(0\),最后按题目要求将所有答案异或后输出。

复杂度

  • 时间复杂度:\(O((n+m+q)\log n)\)
  • 空间复杂度:\(O(n+m)\)
namespace DS {
	struct sgt {
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
		int tr[4 * maxn + 5];
		void bld(const vector<int> &a0,int u,int l,int r) {
			if (l == r) return tr[u] = a0[l],void();
			int mid = (l + r) >> 1;
			bld(a0,ls(u),l,mid),bld(a0,rs(u),mid + 1,r);
			tr[u] = max(tr[ls(u)],tr[rs(u)]);
		}
		void upd(int u,int l,int r,int p,int v) {
			if (l == r) return tr[u] = v,void();
			int mid = (l + r) >> 1;
			if (p <= mid) upd(ls(u),l,mid,p,v);
			else upd(rs(u),mid + 1,r,p,v);
			tr[u] = max(tr[ls(u)],tr[rs(u)]);
		}
		int qry(int u,int l,int r,int ql,int qr) {
			if (ql <= l && r <= qr) return tr[u];
			int mid = (l + r) >> 1,ans = 0;
			if (ql <= mid) ans = max(ans,qry(ls(u),l,mid,ql,qr));
			if (qr > mid) ans = max(ans,qry(rs(u),mid + 1,r,ql,qr));
			return ans;
		}
		void print(int u,int l,int r) {
			if (l == r) return cout << tr[u] << " ",void();
			int mid = (l + r) >> 1;
			print(ls(u),l,mid),print(rs(u),mid + 1,r);
		}
	};
	struct sgt2 {
		int tg[4 * maxn + 5];
		void update(int u,int l,int r,int ql,int qr,int v) {
			if (ql <= l && r <= qr) return tg[u] = v,void();
			int mid = (l + r) >> 1;
			if (tg[u]) tg[ls(u)] = tg[u],tg[rs(u)] = tg[u],tg[u] = 0;
			if (ql <= mid) update(ls(u),l,mid,ql,qr,v);
			if (qr > mid) update(rs(u),mid + 1,r,ql,qr,v);
		}
		int qry(int u,int l,int r,int p) {
			if (l == r) return tg[u];
			int mid = (l + r) >> 1;
			if (tg[u]) tg[ls(u)] = tg[u],tg[rs(u)] = tg[u],tg[u] = 0;
			if (p <= mid) return qry(ls(u),l,mid,p);
			return qry(rs(u),mid + 1,r,p);
		}
		void print(vector<int> &res,int u,int l,int r) {
			if (l == r) return res[l] = tg[u],void();
			int mid = (l + r) >> 1;
			if (tg[u]) tg[ls(u)] = tg[u],tg[rs(u)] = tg[u],tg[u] = 0;
			print(res,ls(u),l,mid),print(res,rs(u),mid + 1,r);
		}
	};
}
namespace Denia {
	struct seq { int l,r,id; };
	int n,m,q;
	int c[maxn + 5];
	seq s[maxn + 5],ss[maxn + 5];
	DS::sgt sgt;
	DS::sgt2 sgtc;
	set<int> book[maxn + 5];
	int ans = 0;
	void upd(int p,int v) {
		int old = c[p];
		auto ip = book[old].lower_bound(p);
		auto ipre = ip,inxt = ip; ipre --,inxt ++;
		if (inxt != book[old].end()) { // 有后继->有影响
			if (ip == book[old].begin()) sgt.upd(1,1,n,*inxt,0); // 不存在前驱
			else sgt.upd(1,1,n,*inxt,*ipre); // 存在前驱
		}
		book[old].erase(p);
		c[p] = v; book[v].insert(p);
		ip = book[v].lower_bound(p);
		ipre = ip,inxt = ip; ipre --,inxt ++;
		if (ip != book[v].begin()) sgt.upd(1,1,n,p,*ipre);
		else sgt.upd(1,1,n,p,0);
		if (inxt != book[v].end()) sgt.upd(1,1,n,*inxt,p); // 有后继->有影响
	}
	int Fa[maxn + 5]; // 父亲
	int fa[maxn + 5]; // 并查集
	int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
	void del(int x) { fa[x] = find(Fa[fa[x]]); }
	int L[maxn + 5];
	void jump(int u,int t) {
		if (u == 0) return ;
		if (sgt.qry(1,1,n,s[u].l,s[u].r) >= s[u].l) return ;
		if (L[u] > t) L[u] = t;
		del(u);
		jump(find(Fa[u]),t);
	}
	void Main() {
		cin >> n >> m >> q;
		vector<int> pre0(n + 1);
		for (int i = 1;i <= n;i ++) {
			cin >> c[i];
			if (!book[c[i]].empty()) {
				auto p = book[c[i]].end(); p --;
				pre0[i] = *p;
			} else pre0[i] = 0;
			book[c[i]].insert(i);
		}
		sgt.bld(pre0,1,1,n);
		for (int i = 1;i <= m;i ++) {
			s[i].id = i;
			cin >> s[i].l >> s[i].r;
			ss[i] = s[i];
		}
		sort(ss + 1,ss + m + 1,[](const seq &x,const seq &y) {
			if (x.l == y.l) return x.r > y.r;
			return x.l < y.l;
		});
		// 建树
		for (int i = 1;i <= m;i ++) {
			Fa[ss[i].id] = sgtc.qry(1,1,n,ss[i].l);
			sgtc.update(1,1,n,ss[i].l,ss[i].r,ss[i].id);
		}
		vector<int> pos(n + 1);
		sgtc.print(pos,1,1,n); // 每个位置对于的树上的点
		for (int i = 1;i <= m;i ++) {
			if (sgt.qry(1,1,n,ss[i].l,ss[i].r) < ss[i].l) L[ss[i].id] = 0;
			else L[ss[i].id] = m + ss[i].id;
		}
		sgt.bld(pre0,1,1,n);
		for (int i = 1;i <= n;i ++) fa[i] = i;
		for (int t = 1;t <= q;t ++) {
			int x,y; cin >> x >> y;
			upd(x,y);
			int id = pos[x];
			jump(find(id),t);
		}
		for (int i = 1;i <= n;i ++) ans = ans ^ L[i];
		cout << ans << "\n";
	}
}

posted @ 2026-08-30 09:56  Denia-kawaii  阅读(7)  评论(0)    收藏  举报