P11024 定序 Redoslijed
简要题意:
在一个长度为 \(n\) 的木板上画画,现在已知他画了 \(m\) 次,每次在 \(l_i\) 到 \(r_i\) 上覆盖成颜色 \(c\)。他不知道这 \(m\) 次的顺序,但他知道最后的状态,试构造一种操作顺序使得操作后木板的状态为给定状态,或无解。
解法:
首先可以发现正着来做明显会产生后效性,我们选择倒着来做。对于每一个木板涂色状态,我们该怎么快速查询是否有颜色可以去掉?(即在涂上后可以是当前状态的覆盖)我们用线段树维护一个值 \(c_cur\),表示在 \(cur\) 的线段树区间内不同情况的值:
- 在当前这个区间有不同颜色时,\(c=-1\)
- 在这个区间全部都是不能涂的时,\(c=\infty\)
- 在这个区间全部都是之前涂过的,可以随便涂时,\(c=0\)
- 否则,除了可以随便涂的就只剩单一颜色 \(s\) 时,\(c=s\)
我们对每个操作计算一个值 \(cnt\),表示在线段树中包含的最大区间(即不包含区间下面的小区间)中有多少个是不符合这个操作的(不可以直接去掉该操作),这样我们就可以在后面的操作中对于每个区间让包含它的 \(cnt\) 减 \(1\),然后如果 \(cnt\) 归 \(0\) 了就说明这个操作不再有任何的阻碍,就把它加入队列中,并把所有它包含的位置全部归为 \(0\)。
Trick:要是每个操作的每个位置都更新,那就变成 \(O(n^2\log n)\) 了,不如没优化。所以我们用并查集进行优化,对于已经设置成 \(0\) 的点就并上它的下一个点,这样就不会再次更新到。
Code:
#include <bits/stdc++.h>
#define ll long long
#define ls cur << 1
#define rs cur << 1 | 1
using namespace std;
const int N = 5e5 + 10, inf = 1e9;
int n, m, l[N], r[N], c[N], b[N], fa[N];
int tr[N << 2], cnt[N];
int q[N], head = 1, tail;
vector<int> v[N << 2];
int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);}
int merge(int x, int y) {
if(!x) return y;
if(!y) return x;
if(x == y) return x;
return -1;
}
void build(int cur, int l, int r) {
if(l == r) {
tr[cur] = b[l] ? b[l] : inf;
return ;
}
int mid = l + r >> 1;
build(ls, l, mid); build(rs, mid + 1, r);
tr[cur] = merge(tr[ls], tr[rs]);
}
void insert(int cur, int l, int r, int ql, int qr, int id) {
if(ql <= l && r <= qr) {
v[cur].push_back(id);
if(tr[cur] && tr[cur] != c[id]) cnt[id]++;
return;
}
int mid = l + r >> 1;
if(ql <= mid) insert(ls, l, mid, ql, qr, id);
if(qr > mid) insert(rs, mid + 1, r, ql, qr, id);
}
void upd(int cur, int l, int r, int pos) {
int lst = tr[cur];
if(l == r) tr[cur] = 0;
else {
int mid = l + r >> 1;
if(pos <= mid) upd(ls, l, mid, pos);
else upd(rs, mid + 1, r, pos);
tr[cur] = merge(tr[ls], tr[rs]);
}
if(lst != tr[cur]) {
for(int id : v[cur]) {
if(lst && lst != c[id] && (tr[cur] == 0 || tr[cur] == c[id])) {
if(--cnt[id] == 0) q[++tail] = id;
}
}
}
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++) scanf("%d%d%d", &l[i], &r[i], &c[i]);
for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
for(int i = 1; i <= n + 1; i++) fa[i] = i;
build(1, 1, n);
for(int i = 1; i <= m; i++) insert(1, 1, n, l[i], r[i], i);
for(int i = 1; i <= m; i++)
if(!cnt[i]) q[++tail] = i;
while(head <= tail) {
int u = q[head++];
for(int i = find(l[u]); i <= r[u]; i = find(i + 1)) {
upd(1, 1, n, i); fa[i] = i + 1;
}
}
if(tail == m && (tr[1] == 0 || tr[1] == inf)) {
puts("DA");
for(int i = m; i >= 1; i--) printf("%d ", q[i]);
} else puts("NE");
return 0;
}

浙公网安备 33010602011771号