题意:给你一个n个点m条边的带权有向图,让你翻转一些边使得这个图无环,翻转的代价是所有边权中的最大值,你需要让这个代价最小。
首先在图论中有这样一个结论:若给一张无环的有向图,让你给图中某些点对间连有向边,总有一种方案使得图依旧是无环的。
因为将原图进行拓扑排序后,对于每个点对,从拓扑序较小的点连向拓扑序较大的点,若拓扑序相同则随意。图一定的无环的。
因此我们就将题意转化为:在给出的图中删去一些边,使得图中没有环,同时要删去的最大边的权值最小。
之所以可以这么转化,是因为如果从删去相应边之后的图中开始“加边”,那么就可以把删去的边加上,如果会产生环就改变这条边的方向,根据上面的结论,一定可以使得新的图无环。
最大值最小,想到可以二分一下答案。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<map>
#define LL long long
using namespace std;
const LL inf=0x3f3f3f3f;
const LL maxn=2e5+5;
inline void _read(LL &x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9')
{if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
struct Edge{
LL from,to,d;
};
LL n,m,mark[maxn],pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
stack<int>S;
vector<LL>G[maxn];
vector<Edge>edges;
void dfs(LL u){
pre[u]=lowlink[u]=++dfs_clock;
S.push(u);
for(LL i=0;i<G[u].size();i++){
if(mark[G[u][i]])continue;
Edge e=edges[G[u][i]];
LL v=e.to;
if(!pre[v]){
dfs(v);
lowlink[u]=min(lowlink[u],lowlink[v]);
}
else if(!sccno[v])lowlink[u]=min(lowlink[u],pre[v]);
}
if(lowlink[u]==pre[u]){
scc_cnt++;
for(;;){
int x=S.top();S.pop();
sccno[x]=scc_cnt;
if(x==u)break;
}
}
}
bool check(LL k){
memset(mark,0,sizeof(mark));
memset(pre,0,sizeof(pre));
memset(lowlink,0,sizeof(lowlink));
memset(sccno,0,sizeof(sccno));
dfs_clock=scc_cnt=0;
for(LL i=0;i<edges.size();i++)
if(edges[i].d<=k)mark[i]=1;
for(LL i=1;i<=n;i++)
if(!pre[i])dfs(i);
return scc_cnt==n;
}
int main(){
_read(n);_read(m);
for(LL i=1;i<=m;i++){
LL x,y,z;
_read(x);_read(y);_read(z);
edges.push_back((Edge){x,y,z});
G[x].push_back(edges.size()-1);
}
LL L=0,R=2e9+3;
while(L<R){
LL mid=(L+R)>>1;
if(check(mid))R=mid;
else L=mid+1;
}
cout<<R;
}