CF1100E
题意:
n个点,m条边的有向图,改变边的方向使之没有环,且改变的边的权值最大值最小。
输出最小权值和边的数量
输出边的编号(边的编号为输入顺序)。如果有很多,输入任意。
题解:
显然二分枚举权值最大的边。把大于这个权值的边进行拓扑排序。因为小≤这个权值的边可以任意改变方向,所以看成无环。然而大于这个权值的边不能改变,所以拓扑排序如果存在环,那么就false了。最后该改变哪些边的方向呢?如果这条边从x指向y,且x的拓扑序大于y,那么就改变。如果指向沿着拓扑序的大小,显然不可能存在换。
代码:
#include <bits/stdc++.h>
using namespace std;
int const N = 100000 + 10;
int const MAX = 1e9 + 10;
int n,m,res;
int from[N],to[N],dist[N];
int indegree[N],topo[N];
vector<int>ans;
vector<int>G[N];
bool Judge(int mid){
for(int i=1;i<=n;i++) G[i].clear(), indegree[i] = 0;
for(int i=1;i<=m;i++){
if(dist[i] <= mid) continue;
indegree[to[i]]++;
G[from[i]].push_back(to[i]);
}
int cnt = 0;
queue<int>q;
for(int i=1;i<=n;i++) if(indegree[i] == 0) q.push(i), topo[i] = ++cnt;
while(!q.empty()){
int p = q.front(); q.pop();
for(int i=0;i<G[p].size();i++){
int t = G[p][i];
if(--indegree[t] == 0) q.push(t), topo[t] = ++cnt;
}
}
if(cnt != n) return false;
ans.clear();
for(int i=1;i<=m;i++){
if(dist[i] <= mid && topo[from[i]] > topo[to[i]])
ans.push_back(i);
}
return true;
}
int main(){
scanf("%d%d",&n,&m); //n个点,m条边
for(int i=1;i<=m;i++)
scanf("%d%d%d",&from[i],&to[i],&dist[i]);
int l= 0, r = MAX, res;
while(l <= r){
int mid = (l + r) >> 1;
if(Judge(mid)){
r = mid - 1;
res = mid;
}else l = mid + 1;
}
printf("%d %d\n",res,ans.size());
for(int i=0;i<ans.size();i++) printf("%d ",ans[i]); printf("\n");
return 0;
}