题解:P11024 [COTS 2020] 定序 Redoslijed
VainSylphid · · 题解
有意思的题。
正着考虑非常困难!这样我没法知道该用哪次染色操作。但是如果我们考虑最后一次染色
类似地从后往前考虑,我们能做某次染色
并且,可以发现,如果有多个操作可以同时放在这个位置,那么任选一个操作不会使有解的情况变成无解,因为已经被染过的位置只会越来越多,可以用的操作也会越来越多,原本可以用的操作还是可以用。
那么现在我们就得到了这样一个构造方法:从最后一次操作往前考虑,每次找到一个操作
显然这样的构造一定合法,并且只要有解,一定能通过这样的构造找到一个解。
但是这怎么和暴力同分!我们发现这个过程和拓扑排序有点像,不如看看能不能快速维护有哪些合法的操作
我们发现,对于线段树上的一个区间
如果一个操作
-
取出队头的一个操作
(l,r,c) ,如果没有这样的操作就报告无解。 -
把
b_l\sim b_r 全部改成一个你喜欢的标记,比如-1 。因为不能用懒标记,所以要暴力递归找到区间中所有没标记过的位置。 -
pushup 的时候,如果当前区间首次只剩下一种没标记的颜色,那么把这个区间上挂着的相同颜色的操作的
cnt 减一。 -
如果当前区间全部被标记了,那么把这个区间上挂着的所有操作的
cnt 减一,但是之前就已经同色的那些区间不能减,需要记录下首次只剩下一种没标记的颜色编号,这次减的时候就跳过了。 -
如果一个操作的
cnt 被减到0 ,让它入队。
这样一来,每个操作被拆成
代码并不难写。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,xl[500005],xr[500005],c[500005],b[500005];
int d[2000005],cnt[500005];
int vis[2000005],cl[2000005];
vector<int> v[2000005];
queue<int> q;
int ans[500005],pos;
void insert(int p,int l,int r,int id)
{
if(xl[id] <= l && r <= xr[id])
{
v[p].push_back(id),cnt[id]++;
return;
}
int mid = l + r >> 1;
if(xl[id] <= mid)
insert(p << 1,l,mid,id);
if(xr[id] > mid)
insert(p << 1 | 1,mid + 1,r,id);
}
void update(int p)
{
if(d[p] == -2 && vis[p] < 2)
{
vis[p] = 2;
for(auto i : v[p])
if(cl[p] != c[i])
{
if(!(--cnt[i]))
q.push(i);
}
}
if(d[p] != -1 && vis[p] < 1)
{
cl[p] = d[p],vis[p] = 1;
for(auto i : v[p])
if(cl[p] == c[i])
{
if(!(--cnt[i]))
q.push(i);
}
}
}
void pushup(int p)
{
if(d[p << 1] == -1 || d[p << 1 | 1] == -1)
d[p] = -1;
else if(d[p << 1] == -2)
d[p] = d[p << 1 | 1];
else if(d[p << 1 | 1] == -2)
d[p] = d[p << 1];
else
d[p] = (d[p << 1] == d[p << 1 | 1] ? d[p << 1] : -1);
update(p);
}
void build(int p,int l,int r)
{
if(l == r)
{
d[p] = b[l];
update(p);
return;
}
int mid = l + r >> 1;
build(p << 1,l,mid);
build(p << 1 | 1,mid + 1,r);
pushup(p);
}
void modify(int p,int l,int r,int x,int y)
{
if(x <= l && r <= y && d[p] == -2)
return;
if(l == r)
{
d[p] = -2;
update(p);
return;
}
int mid = l + r >> 1;
if(x <= mid)
modify(p << 1,l,mid,x,y);
if(y > mid)
modify(p << 1 | 1,mid + 1,r,x,y);
pushup(p);
}
int query(int p,int l,int r,int x)
{
if(l == r) return d[p];
int mid = l + r >> 1;
if(x <= mid) return query(p << 1,l,mid,x);
return query(p << 1 | 1,mid + 1,r,x);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++)
scanf("%d%d%d",&xl[i],&xr[i],&c[i]),insert(1,1,n,i);
for(int i = 1;i <= n;i++)
scanf("%d",&b[i]);
build(1,1,n);
while(!q.empty())
{
int tmp = q.front();
q.pop();
ans[++pos] = tmp;
modify(1,1,n,xl[tmp],xr[tmp]);
}
for(int i = 1;i <= n;i++)
if(query(1,1,n,i) != -2 && query(1,1,n,i) != 0)
{
printf("NE\n");
return 0;
}
if(pos != m)
printf("NE\n");
else
{
printf("DA\n");
for(int i = m;i >= 1;i--)
printf("%d%c",ans[i]," \n"[i == 1]);
}
return 0;
}