题目链接:http://codeforces.com/contest/1100/problem/E
题目大意:给出一个n个点,m条边的有向图。每条边都有边权,现在要求翻转若干条边的方向,使得这个图变成一个DAG,问要如何翻转边,才能使得被翻转的边中最大的权值最小化。
题目思路:比赛的时候看了半天也没看懂这个题目第一问要输出的是什么,所以就直接跑去死磕F题了(最后F题也没磕出来,真的菜QAQ)
这个题由于是要求最小化最大值,所以我们就考虑二分求翻转的边中最大的权值是多少。要判断一个有向图中是否有环,我们自然就想到用拓扑排序了。在二分过程中,我们只将边权大于check值的边加入图中(因为边权小于等于check值的后面都是要进行翻转的,所以我们就暂时不用管),接下来再进行拓扑排序判断是否还存在环就行了。最后再根据拓扑排序后的顺序关系,判断有哪些边是要进行翻转的,将方案数输出就可以了。(二分结束之后记得再跑一遍拓扑排序,因为二分结束前最后一次跑的拓扑排序并不一定是正确的那个拓扑排序的顺序)。
具体实现看代码:
#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
const int MX = 1e5 + 5;
int n, m;
vector<int>G[MX], ans;
int in[MX], pos[MX];
struct edge {
int u, v, w;
} E[MX];
bool check(int cost) {
for (int i = 1; i <= n; i++) G[i].clear(), in[i] = 0;
for (int i = 1; i <= m; i++) {
if (E[i].w <= cost) continue;
G[E[i].u].pb(E[i].v);
in[E[i].v]++;
}
queue<int>q;
for (int i = 1; i <= n; i++) if (in[i] == 0) q.push(i);
int sz = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
pos[u] = ++sz;
for (auto v : G[u]) {
in[v]--;
if (in[v] == 0) q.push(v);
}
}
return sz == n;
}
int main() {
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
#endif
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
int l = 0, r = 1e9;
int res = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
res = mid;
r = mid - 1;
} else
l = mid + 1;
}
check(res);
for (int i = 1; i <= m; i++)
if (E[i].w <= res && pos[E[i].u] > pos[E[i].v]) ans.pb(i);
printf("%d %d\n", res, (int)ans.size());
for (auto x : ans) printf("%d ", x);
printf("\n");
return 0;
}