大艺术家
题意
给定一个长度为 \(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";
}
}

浙公网安备 33010602011771号