E. Andrew and Taxi
题意
给你一个有边权的有向图,反转一条边的代价是这条边的边权,反转多个边的代价是所有反转边里面边权最大的那条边的边权,问让这个图不存在环的最小代价,以及被反转的边的编号。
做法
首先求最小代价,我们只需要二分这个代价,check就是看删除所有小于这个代价的边之后是否有环。
输出边集,要用到拓扑排序的一个性质
一个图进行拓扑排序后,对于每一个有向边u->v都存在u的拓扑序<v的拓扑序,则这个图上无环
之后我们就枚举所有小于当前代价的边,对所有不满足条件的边反转就可以。
这样就保证反转之后无环,而且代价最小。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
typedef pair <int, int> pii;
const int maxn = 1e5+5;
#define Se second
#define Fi first
int n,m;
vector<pii> G[maxn];
int top[maxn];
int ind[maxn];
int que[maxn];
bool check(int mid)
{
int qt=0;for(int i=1;i<=n;i++) ind[i]=0;for(int i=1;i<=n;i++){
for(int j=0;j<G[i].size();j++){
if(G[i][j].Se>mid) ind[G[i][j].Fi]++;}}for(int i=1;i<=n;i++){
if(ind[i]==0){
que[qt++]=i;top[i]=qt;}}for(int i=0;i<qt;i++){
int u=que[i];for(int j=0;j<G[u].size();j++){
if(G[u][j].Se<=mid) continue;if(--ind[G[u][j].Fi]==0){
que[qt++]=G[u][j].Fi;top[G[u][j].Fi]=qt;}}}for(int i=1;i<=n;i++) if(ind[i]) return false;return true;
}
struct Edge
{
int u,v,w;Edge(){
}Edge(int uu,int vv,int ww){
u=uu;v=vv;w=ww;}
}E[maxn];
vector<int> ans;
int main()
{
scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);G[u].push_back(pii(v,w));E[i]=Edge(u,v,w);}int l=0,r=1000000000,mid;while(l<=r){
mid=(l+r)/2;if(check(mid)) r=mid-1;else l=mid+1;}check(l);for(int i=1;i<=m;i++){
int fi=E[i].u;int to=E[i].v;if(E[i].w<=l&&top[fi]>top[to]) ans.push_back(i);}printf("%d %d\n",l,ans.size());for(int i=0;i<ans.size();i++){
printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');}return 0;
}