CF1100E Andrew and Taxi 题解

题意

⼀个有向图,改变⼀些边的⽅向,使得图中没有环,求使得所选边边权最大值最⼩的⽅案。

分析

如果边权最大值小的可行,那最大值更大的也一定可行。因此可以二分边权最大值,每次把边权比最大值大的加入图中,如果没有环,那把所有没加的边的方向都改成拓扑序的方向就一定可以满足,如果有环说明不可行。二分出来最小的最大值后,重新建图,把所有与拓扑序相反的边反转方向即可。

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline void qread(){}template<class T1,class ...T2>
inline void qread(T1 &a,T2&... b)
{
    register T1 x=0;register bool f=false;char ch=getchar();
    while(ch<'0') f|=(ch=='-'),ch=getchar();
    while(ch>='0') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    x=(f?-x:x);a=x;qread(b...);
}
template<class T1,class T2>inline T1 qmax(const T1 &x,const T2 &y){return x>y?x:y;}
template<class T1,class T2>inline T1 qmin(const T1 &x,const T2 &y){return x<y?x:y;}
const int MAXN=1e5+7;
int n,m,head[MAXN];
struct Edge
{
    int to,nxt,dis,id;
    Edge(){;}Edge(int _to,int _dis,int _nxt,int _id):to(_to),dis(_dis),nxt(_nxt),id(_id){}
};vector<Edge>edge;
void add_edge(int u,int v,int w,int id){edge.push_back(Edge(v,w,head[u],id));head[u]=edge.size()-1;}
int in[MAXN],ans=2139062143;
vector<int>topo;
int topo_sort()
{
    topo.clear();int i;queue<int>q;
    for(i=1;i<=n;i++) if(!in[i]) q.push(i);
    while(!q.empty())
    {
        int u=q.front();q.pop();topo.push_back(u);
        for(i=head[u];i!=-1;i=edge[i].nxt) if(!(--in[edge[i].to])) q.push(edge[i].to);
    }
    return topo.size();
}
int x[MAXN],y[MAXN],z[MAXN];
void make(int mid) // 建图
{
    edge.clear();mem(head,-1);mem(in,0);
    for(int i=0;i<m;i++)
        if(z[i]>mid) add_edge(x[i],y[i],z[i],i+1),in[y[i]]++;
}
bool check(int mid)
{
    make(mid);
    return topo_sort()==n; // 如果topo数组中包含所有点,说明没有环
}
int mp[MAXN];set<int>ans1;
int main()
{
    qread(n,m);int i,j;
    int l=0,r=0;
    for(i=0;i<m;i++) qread(x[i],y[i],z[i]),r=qmax(r,z[i]); // 输入时将二分的上限定为最大边权
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) r=mid-1,ans=qmin(ans,mid);
        else l=mid+1;
    }
    check(ans);
    for(int i=0;i<topo.size();i++) mp[topo[i]]=i; // 记录每个点在拓扑序中的位置
    make(0); // 重建图,加入每条边
    for(i=1;i<=n;i++)
        for(j=head[i];j!=-1;j=edge[j].nxt)
            if(mp[i]>mp[edge[j].to]) ans1.insert(edge[j].id); // 该边方向与拓扑序相反
    printf("%d %d\n",ans,ans1.size());
    for(auto i:ans1) printf("%d ",i);
    return 0;
}
posted @ 2022-02-17 13:39  l_x_y  阅读(21)  评论(0编辑  收藏  举报