題目鏈接: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;
}