题解 CF825E【Minimal Labels】
偶然间翻到三个月前写的这个题,发现现有的题解均未给出解法的正确性证明,只是不明不白地写了一些对理解做法毫无帮助的话。我认为解法的正确性并不显然,因此这篇题解主要给出正确性证明,补上逻辑漏洞。
解法与其他题解一样,即:建反图,然后跑拓扑排序,每次优先取出可以取出的编号最大的点,从
这似乎有点反直觉:为什么倒着做就恰好能满足字典序最小呢?相信大多数人的第一反应都是正着做,这也是我认为正确性并不显然的原因。
证明:
以下“入边”“出边”“入度”“出度”等均为原图上的。
考虑整个算法的第一步,也就是分配权值
我们记在上述算法中,被分配了权值
对于这种分配方案中被分配权值
因此,我们可以重新分配权值使得
由于点
//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=(y);x<=(z);x++)
#define per(x,y,z) for(int x=(y);x>=(z);x--)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
#define likely(exp) __builtin_expect(!!(exp), 1)
#define unlikely(exp) __builtin_expect(!!(exp), 0)
using namespace std;
typedef long long ll;
const int N = 1e5+5;
int n, m, deg[N], id[N], tms;
vector<int> e[N];
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
void toposort() {
priority_queue<int> q;
rep(i, 1, n) if(!deg[i]) q.push(i);
while(!q.empty()) {
int u = q.top(); q.pop();
id[u] = ++tms;
for(int v : e[u]) if(!--deg[v]) q.push(v);
}
}
int main() {
scanf("%d%d", &n, &m);
rep(i, 1, m) {
int u, v;
scanf("%d%d", &u, &v);
e[v].push_back(u);
++deg[u];
}
toposort();
rep(i, 1, n) printf("%d%c", n+1-id[i], " \n"[i==n]);
return 0;
}