【题解 && 图论建模】 航空管制

题目传送门

题目描述:

【题解 && 图论建模】 航空管制_第1张图片


Solution

看到第一个限制,我们就能想到连边。

但是,我们如果建正图,很显然对于多个关系会难以处理。
因此,我们考虑建反图。

如果一个序列是好的,那么必然满足逆拓扑序。
我们用堆来维护当前的拓扑序。
如果当前点的最晚限制是 k [ i ] k[i] k[i],那么在当前这个点之前一定要有 n − k [ i ] n-k[i] nk[i]个点。
用小根堆维护 n − k [ i ] n-k[i] nk[i],对于第一问我们不断做拓扑序即可。

考虑第二问
将问题转化,对于当前数 x x x,它所在的位置下标最小,就是在反图中它出现的位置尽可能靠后。
所以,如果我们在拓扑序中第一次遍历到了 x x x,我们不考虑将 x x x入队,继续做拓扑序,直到找到第一个不满足条件的点,就是它在反图中能出现的最晚位置。这样便是 x x x出现的最早位置。

但是。。显然这样的复杂度不能通过这题
所以我们开个 O 2 O2 O2就好啦!


Code

#include
using namespace std;

const int N = 2e4+10;
typedef pair < int , int > pii;

struct Node{
	int y,Next;
}e[2*N];
int len , linkk[N];

int n,m;
int k[N] , in[N] , In[N];
int ans[N] , cnt;
priority_queue < pii , vector < pii > , greater < pii > > q;

void Insert(int x,int y){
	e[++len] = (Node){y,linkk[x]};
	linkk[x] = len;
}

void Topsort(){
	for (int i = 1; i <= n; i++) in[i] = In[i];
	for (int i = 1; i <= n; i++)
	  if (!in[i]) q.push({n-k[i],i});
	while (!q.empty()){
		int x = q.top().second; q.pop();
		ans[++cnt] = x;
		for (int i = linkk[x]; i; i = e[i].Next){
			int y = e[i].y;
			in[y]--;
			if (in[y] == 0) q.push({n-k[y],y});
		}
	}
}

int Solve(int xx){
	while (!q.empty()) q.pop();
	for (int i = 1; i <= n; i++) in[i] = In[i];
	int Ans = 0;
	for (int i = 1; i <= n; i++)
	  if (!in[i]) q.push({n-k[i],i});
	while (!q.empty()){
		int x = q.top().second; q.pop();
		if (x == xx) continue;
		if (n-Ans > k[x]) return n-Ans;
		Ans++;
		for (int i = linkk[x]; i; i = e[i].Next){
			int y = e[i].y;
			in[y]--;
			if (in[y] == 0) q.push({n-k[y],y});
		}
	}
	return n-Ans;
}

int main(){
	scanf("%d %d",&n,&m);
	for (int i = 1; i <= n; i++) scanf("%d",&k[i]);
	for (int i = 1,x,y; i <= m; i++)
	  scanf("%d %d",&x,&y) , Insert(y,x) , In[x]++;
	Topsort();
	for (int i = cnt; i; i--) printf("%d ",ans[i]);
	printf("\n");
	for (int i = 1; i <= n; i++)
	  printf("%d ",Solve(i));
}

你可能感兴趣的:(建图,题解)