二分+dfs判环+拓扑序
我们的目标是以最小的代价,反转边破坏所有的环
首先可以发现题目中的$traffic$ $controllers$的数量为所反转边的最大权值
那么所以边权小于等于$traffic$ $controllers$的数量的边是都可以反转的
那么对于边权大于$traffic$ $controllers$的数量的边构成的图只要没有环,就满足条件了
因为对于可以反转的边只要不能反转的边无环,总有方案可以使整张图无环
那么对于已知$traffic$ $controllers$的数量,可以在$O(n)$的时间内判断是否可行
因此可以将每条边排序,在二分最小$traffic$ $controllers$的数量
那么前半部分完成了
现在得到了以不可反转的边构成的图,求一种反转方案
就对剩下的节点进行拓扑排序
那么可以发现只要拓扑序大的点连向拓扑序小的节点,就一定我形成环
因此将这种边反转即可
所以总复杂度$O(nlogm+n+m)$
#include <bits/stdc++.h> using namespace std; const int MAXN=100100; int n,m,first[MAXN],point[MAXN*2],len[MAXN*2],wh[MAXN*2]; int nxt[MAXN*2],tot,vi[MAXN],si[MAXN],dfn[MAXN]; bool bl; queue <int> q; vector <int> ans; struct node { int u,v,c,wh; }sh[MAXN]; void add_edge(int x,int y,int l,int w) { tot++; nxt[tot]=first[x]; first[x]=tot; point[tot]=y; len[tot]=l; wh[tot]=w; } bool cmp(node a,node b) { return a.c<b.c; } void dfs(int x,int MAX) { if (!bl) return; vi[x]=2; for (int i=first[x];i!=-1;i=nxt[i]) { if (len[i]<=MAX) continue; if (vi[point[i]]==2)//表示有环 { bl=0; break; } dfs(point[i],MAX); } vi[x]=1; } bool check(int mid) { int MAX=sh[mid].c; memset(vi,0,sizeof(vi)); bl=1; for (int i=1;i<=n;i++) { if (!vi[i])//对于每一个节点进行遍历 { dfs(i,MAX); if (!bl) return 0; } } return 1; } int main() { memset(first,-1,sizeof(first)); memset(nxt,-1,sizeof(nxt)); scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { scanf("%d%d%d",&sh[i].u,&sh[i].v,&sh[i].c); sh[i].wh=i; add_edge(sh[i].u,sh[i].v,sh[i].c,i); } sort(sh+1,sh+1+m,cmp); int l,r; r=m;l=0;//二分枚举下标 while (l<r) { int mid; mid=(l+r)/2; if (check(mid)) r=mid; else l=mid+1; } int per; per=sh[r].c; if (per==0) { printf("%d %d ",per,0); return 0; } for (int i=1;i<=n;i++) { for (int j=first[i];j!=-1;j=nxt[j]) { if (len[j]>per) { si[point[j]]++; } } } for (int i=1;i<=n;i++) { if (si[i]==0) { q.push(i); } } int tot=0; while (!q.empty()) { int f; f=q.front(); q.pop(); tot++; dfn[f]=tot;//记录拓扑序 for (int i=first[f];i!=-1;i=nxt[i]) { if (len[i]<=per) continue; si[point[i]]--; if (si[point[i]]==0 && dfn[point[i]]==0) { q.push(point[i]); } } } for (int i=1;i<=m;i++) { if (sh[i].c>per) break; if (dfn[sh[i].u]>dfn[sh[i].v])//反转边 ans.push_back(sh[i].wh); } printf("%d %d ",per,(int)ans.size()); for (int i=0;i<(int)ans.size();i++) printf("%d ",ans[i]); printf(" "); }