<h1>题目描述</h1>
题目链接
有 $n$ 个航班依次起飞,一个时刻只能有一个飞机起飞,并且有 $m$ 个限制:
- 第一种限制,第 $i$ 个飞机必须在 $c_i$ 的时刻前起飞。
- 第二种限制,第 $i$ 个飞机必须在第 $j$ 个飞机之前起飞。
询问: - 一个可行的起飞方案。
- 每个飞机最早的起飞时间。
$ n \leq 2 \times 10^3, m \leq 10^4$
<h1>题解</h1>
首先这是一道十分套路的反图拓扑排序。
举例:如果想求出一张有向图中标号 $x$ 的节点最靠前的拓扑序,怎么求?
显然不能开个堆直接跑,这是字典序最小的不保证 $x$ 在最前面,但是我们如果建出反图在反图跑最大字典序的话,就一定能让 $x$ 尽量朝后,答案就是把这个反图拓扑序列 reverse 一下。
那么对于第一问,我们按照第二种限制建出反图,显然第一种的每个限制就变成了每一个航班在拓扑序中的位置 $ pos >n-c_i $,然后按照第一种限制将节点扔到小根堆里跑一边拓扑排序最后 reverse 一下就可以了。
对于第二问,我们考虑在反图中遍历到 $x$ 节点的时候,一直等到有一个约束失效的时候在让 $x$ 入队,这样就能保证 $x$ 尽量靠后(reverse 后尽量靠前)。
时间复杂度瓶颈在于第二种询问,为 $O(n^2)$
<h1>代码</h1>
#include <algorithm> #include <iostream> #include <cstring> #include <climits> #include <cstdlib> #include <cstdio> #include <vector> #include <queue> #include <stack> #include <map> #include <set> #define Re register #define LL long long #define CLR(a,b) memset(a,b,sizeof(a)) #define FOR(i,a,b) for(Re int i = a;i <= b;++i) #define ROF(i,a,b) for(Re int i = a;i >= b;--i) #define DEBUG(x) std::cerr << #x << '=' << x << std::endl const int MAXN = 2000+5; const int MAXM = 10000+5; struct Node{ struct Edge *firstEdge; int du,num; }node[MAXN]; struct Edge{ Node s,t; Edge *next; }pool[MAXM],*frog = pool; Edge New(Node s,Node *t){ t->du++; Edge *ret = ++frog; ret->s = s;ret->t = t; ret->next = s->firstEdge; return ret; } inline void add(int u,int v){ node[u].firstEdge = New(&node[u],&node[v]); } int N,M; int d[MAXN]; #define P std::pair<int,Node *> #define MP std::make_pair std::priority_queue<P,std::vector<P>,std::greater<P> > q; std::vector<int> ans; inline void topsort(){ FOR(i,1,N) if(!node[i].du) q.push(MP(N-d[i],&node[i])); while(!q.empty()){ Node *v = q.top().second;q.pop(); ans.push_back(v->num); //DEBUG(v->num);DEBUG(v->firstEdge); for(Edge *e = v->firstEdge;e;e = e->next){ if(!--e->t->du) q.push(MP(N-d[e->t->num],e->t)); } } ROF(i,N-1,0) printf("%d ",ans[i]);puts(""); } int t[MAXN]; inline int calc(int x){ int cnt = 0; while(!q.empty()) q.pop(); // q.clear(); FOR(i,1,N) node[i].du = t[i]; FOR(i,1,N) if(!node[i].du) q.push(MP(N-d[i],&node[i])); while(!q.empty()){ Node *v = q.top().second;q.pop(); if(v == &node[x]) continue; if(N-cnt > d[v->num]) return N - cnt; cnt++; for(Edge *e = v->firstEdge;e;e = e->next){ if(!--e->t->du) q.push(MP(N-d[e->t->num],e->t)); } } return N - cnt; } int main(){ read(N);read(M); FOR(i,1,N) read(d[i]),node[i].num = i; FOR(i,1,M){ int u,v;read(u);read(v); add(v,u); } FOR(i,1,N) t[i] = node[i].du; topsort(); FOR(i,1,N) printf("%d ",calc(i));puts(""); return 0; }