題意:給一個有向圖,反轉某些邊讓這個圖無環,反轉的花費是反轉的所有邊中權值最大的,求花費最小的方式,輸出花費和需要反轉的邊。
思路:把某個邊反轉其實就相當於把這條邊刪除,二分答案,用拓撲排序判斷是否又環,最後刪完了之後是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;
}