题意:给一个有向图,反转某些边让这个图无环,反转的花费是反转的所有边中权值最大的,求花费最小的方式,输出花费和需要反转的边。
思路:把某个边反转其实就相当于把这条边删除,二分答案,用拓扑排序判断是否又环,最后删完了之后是ACG,求每个点的拓扑序,然后检验删去的边是否能形成环。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct code
{
int x,y,z;
} edge[100005];
int vis[100005];
int deg[100005],n,m;
vector<int>dap[100005];
queue<int>p;
void init(int mid)
{
for(int i=1; i<=n; i++)
{
deg[i]=0;
dap[i].clear();
}
for(int i=1; i<=m; i++)
{
if(edge[i].z<=mid)
continue;
dap[edge[i].x].push_back(edge[i].y);
deg[edge[i].y]++;
}
}
bool ok()
{
int tim=0;
for(int i=1; i<=n; i++)
{
if(deg[i]==0)
p.push(i);
}
while(!p.empty())
{
int u=p.front();
p.pop();
vis[u]=++tim;
int len=dap[u].size();
for(int i=0; i<len; i++)
{
int to=dap[u][i];
deg[to]--;
if(deg[to]==0)
p.push(to);
}
}
if(tim==n)
return 1;
else
return 0;
}
int main()
{
int l=0,r=1e9;
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
while(l<=r)
{
int mid=(l+r)/2;
init(mid);
if(ok())
r=mid-1;
else
l=mid+1;
}
printf("%d ",l);
int ans=0,s[100005];
init(l);
ok();
for(int i=1; i<=m; i++)
{
if(edge[i].z>l)
continue;
if(vis[edge[i].x]<vis[edge[i].y])
continue;
s[ans++]=i;
}
printf("%d\n",ans);
for(int i=0; i<ans; i++)
{
if(i==0)
printf("%d",s[i]);
else
printf(" %d",s[i]);
}
printf("\n");
return 0;
}