bzoj4010 [HNOI2015]菜肴制作
求出一个 \(n\) 的排列满足 \(m\) 个形如 \((x,\ y)\) 的限定条件,表示 \(x\) 必须在 \(y\) 之前出现,且使得在满足所有限定条件的情况下, \(1\) 尽量优先,满足 \(1\) 尽量优先的情况下 \(2\) 尽量优先……
\(n,\ m\leq10^5\)
图论,拓扑排序,贪心
原题所求显然与最小字典序有差异……
贪心,为了让编号小的尽量往前排,则编号大的要尽量往后排,这样原题就变成了 在满足限定条件的情况下求反序列的最大字典序
把限定条件看做边 \((y,\ x)\) ,求排列无非是在图中跑一遍拓扑排序,满足最大字典序只需用堆维护
这就是所谓的正难则反吧……
时间复杂度 \(O(n\log n)\)
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int Tests;
int n, m, now, cnt, h[maxn], deg[maxn], ans[maxn];
struct edges {
int nxt, to;
edges(int x = 0, int y = 0) :
nxt(x), to(y) {}
} e[maxn];
priority_queue <int> Q;
void addline(int u, int v) {
e[++cnt] = edges(h[u], v), h[u] = cnt;
}
void solve() {
cnt = now = 0;
memset(h, 0, sizeof h);
memset(deg, 0, sizeof deg);
while (!Q.empty()) Q.pop();
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d %d", &x, &y);
addline(y, x), deg[x]++;
}
for (int i = 1; i <= n; i++) {
if (!deg[i]) Q.push(i);
}
while (!Q.empty()) {
int u = Q.top();
ans[++now] = u, Q.pop();
for (int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (!--deg[v]) {
Q.push(v);
}
}
}
for (int i = 1; i <= n; i++) {
if (deg[i] > 0) {
puts("Impossible!");
return;
}
}
for (int i = n; i; i--) {
printf("%d ", ans[i]);
}
putchar(10);
}
int main() {
scanf("%d", &Tests);
while (Tests--) {
solve();
}
return 0;
}