[ABC304Ex] Constrained Topological Sort 做题记录

nn 个点,每个点有 li,ril_i,r_i 两个值,有 mm 条有向边 (si,ti)(s_i,t_i),你要给每个点确定一个权值 aia_i,满足以下条件:

  • aia_inn 的排列;
  • 对于所有 1im1\le i\le m 均有 asi<atia_{s_i}<a_{t_i}
  • 对于所有 1in1\le i\le n 均有 liairil_i\le a_i\le r_i

判断无解。

2n2×1052\le n\le 2\times 10^50mmin(n(n1),4×105)0\le m\le \min(n(n-1),4\times 10^5)i=jsi=sji\not=j\Rightarrow s_i\not=s_j

不难发现若不是 DAG 则无解。

观察到 rsir_{s_i} 实际上可以和 rti1r_{t_i}-1min\min,因为 asi<atia_{s_i}<a_{t_i}。那么先倒着拓扑一遍求出取完 min\minrir_i

先来考虑没有 lil_i 限制该怎么做,显然拓扑排序的时候每次新入队的 rvr_v 都会大于把它入队的 rur_u,那么使用优先队列拓扑,每次选 rur_u 最小的 uu 来拓展一定是最优的。

考虑有 lil_i 限制的情况下怎么办,考虑建立一个“寄存器”vecvec。拓扑前先把所有入度为 00 的点 uu 放进 vecluvec_{l_u},然后从 11nn 遍历,遍历到 ii 时把 vecivec_i 内的所有元素入队,并取出一个元素 xx,让 ansx=ians_x=i 然后拓展 xxvv 入队的时候:

  • lv>il_v>i:先存到 veclvvec_{l_v} 里;
  • lvil_v\le i:直接入队;

这样就能保证 ansilians_i\ge l_i,消除了 lil_i 的限制。

代码如下:

// Problem: [ABC304Ex] Constrained Topological Sort
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/AT_abc304_h
// Memory Limit: 1 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)\

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>

using namespace std;

const int S=200005;

int n,m;
int l[S],r[S];
vector<int> g1[S],g2[S];
int ind[S];
vector<int> idx[S];
int ans[S];

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		g1[x].push_back(y),g2[y].push_back(x);
	}
	for(int i=1;i<=n;i++) scanf("%d%d",&l[i],&r[i]);
	queue<int> q;
	for(int i=1;i<=n;i++) ind[i]=g1[i].size();
	for(int i=1;i<=n;i++) if(ind[i]==0) q.push(i);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int v:g2[u])
		{
			r[v]=min(r[v],r[u]-1);
			if(--ind[v]==0) q.push(v);
		}
	}
	for(int i=1;i<=n;i++) if(ind[i]!=0) return puts("No"),0;
	priority_queue<pair<int,int>> q2;
	for(int i=1;i<=n;i++) ind[i]=g2[i].size();
	for(int i=1;i<=n;i++) if(ind[i]==0) idx[l[i]].push_back(i);
	for(int i=1;i<=n;i++)
	{
		for(int j:idx[i]) q2.push(make_pair(-r[j],j));
		if(q2.empty()) return puts("No"),0;
		int u=q2.top().second;
		q2.pop();
		ans[u]=i;
		for(int v:g1[u])
		{
			if(--ind[v]==0)
			{
				if(l[v]>i) idx[l[v]].push_back(v);
				else q2.push(make_pair(-r[v],v));
			}
		}
	}
	for(int i=1;i<=n;i++) if(ans[i]<l[i]||ans[i]>r[i]) return puts("No"),0;
	puts("Yes");
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	printf("\n");
	return 0;
}