链接:
题意:
给一个有向图,可以修改一些边的方向使得有向图无环,问所修改边的最大权值最小是多少。以及当最大权值最小时最少修改多少条边,修改哪些边。
思路:
和贩神一起打的这一场,当场自闭,根本不是2200难度的题好嘛。。贩神当场有思路(可能还是太强了吧)
说到这道题的做法,贩神能够举一反三,以至于以后见到二分答案的题时总会说出“cf原题啊”。
言归正传,这道题一拿到手感觉就很复杂,胡思乱想瞎找环,找环里权值最大边之类的伪算法肯定是过不去的。
这道题的突破口在于:“最大权值最小值为x时可以,那x+1时肯定更可以”。可以据此二分确定最大权值的最小值:如果最大权值最小值为mid时权值>mid的那些边(不可控的边)成环了,那么一定是mid不够大,如果已经没环了,试试mid能不能再减小。
判环这里用到了dfs判环:设vis有三种状态,0,1,2,0代表未访问,1代表已访问过,2代表正在访问。当dfs到一个2的点时,表示成环了。
确定了最大权值的最小值(ans)之后,保证小于ans的边始终是拓扑序小的向拓扑序大的边连线,就能保证不成环,toposort一下,将拓扑序相反的边加入vector里,vector的size就是最少要翻转几条边了。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);
void redirect(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
}
struct node{
int id,v,w;
node(){}
node(int _id,int _v,int _w){
id = _id,v = _v,w = _w;
}
};
int n,m,ans,cnt;
vector<node>edge[maxn],e[maxn];
int vis[maxn],degree[maxn],order[maxn];
vector<int>out;
bool dfs(int p){
if(vis[p] == 1)return true;
if(vis[p] == 2)return false;
vis[p] = 2;
bool res = true;
for(auto& x : e[p])
if(res)res = min(res,dfs(x.v));
vis[p] = 1;
return res;
}
bool nocircle(){
memset(vis,0,sizeof(vis));
for(int i = 1;i <= n;i++)e[i].clear();
for(int i = 1;i <= n;i++)
for(auto& x : edge[i])
if(x.w > ans)e[i].push_back(x),degree[x.v]++;
for(int i = 1;i <= n;i++)
if(!vis[i] && !dfs(i))return false;
return true;
}
void topo(){
memset(degree,0,sizeof(degree));
queue<int>q;
for(int i = 1;i <= n;i++)
for(auto& x : edge[i])
if(x.w > ans)degree[x.v]++;
for(int i = 1;i <= n;i++)
if(!degree[i])q.push(i);
while(!q.empty()){
int p = q.front();
q.pop();
order[p] = ++cnt;
for(auto x : edge[p])
if(x.w > ans && !--degree[x.v])q.push(x.v);
}
for(int i = 1;i <= n;i++)
if(!order[i])order[i] = ++cnt;
}
int solve(){
topo();
for(int i = 1;i <= n;i++)
for(auto& x : edge[i])
if(x.w <= ans && order[i] > order[x.v])
out.push_back(x.id);
}
int main(){
redirect();
int x,y,z,mx = 0;
scanf("%d %d",&n,&m);
for(int i = 1;i <= m;i++){
scanf("%d %d %d",&x,&y,&z);
mx = max(mx,z);
edge[x].push_back(node(i,y,z));
}
int l = 0,r = mx;
while(l < r){
ans = (l+r)/2;
if(nocircle())r = ans;
else l = ans+1;
}
ans = l;
solve();
int sz = out.size();
sort(out.begin(),out.end());
printf("%d %d\n",ans,sz);
for(int i = 0;i < sz;i++)
printf("%d%c",out[i],i==sz-1?'\n':' ');
return 0;
}