CF1100E Andrew and Taxi 题解

题解 · 2022-12-28

题目意思

给你一个 $n$ 个点,$m$ 条边的有向图,每一个边有一个权值。现在你可以修改任意条边的方向,使得原图变为有向无环图。求修改的边的最大值最小时的方案。

思路

看到最大值最小便很容易想到二分答案(即修改的边中的最大值),我们将这个二分的数设为 $mid$。那么很明显,图中的边会被分为两大类:

  1. 权值大于 $mid$ 的边
  2. 权值小于等于 $mid$ 的边

需要解决的问题:

prob1:对于第一类边我们显然是无法修改方向的,如果存在一个环且换上全是一类边。则这个 $mid$ 显然无法让原图变成有向无环图。

prob2:对于第二类边我们可以改变其方向,直接统计有哪些需要改变方向的边即可。

于是我们十分欣喜的发现,这两个问题都可以用拓扑排序来解决。对于prob1,我们可以将原图的二类边去掉,然后跑拓扑排序判环。对于prob2,我们在prob1的拓扑排序中顺便记录拓扑序,如果出现一个二类边是由拓扑序在后面的点指向拓扑序在前面的点,就说明这条边构成了环,将这条边记录下来即可。

AT LAST 上代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
vector<int>g[maxn];//邻接表
vector<int>ans;//记录方案
int n,m,rd[maxn],f[maxn],u[maxn],v[maxn],dist[maxn];
//u表示一条边的起点,v表示一条边的终点,dist表示一条边的权值
//rd表示一个点的入度,f表示一个点在新图中的拓扑序
bool check(int x){
    for(int i=1;i<=n;i++){rd[i]=0;g[i].clear();}
    for(int i=1;i<=m;i++){//建图(即只保留一类边) 
        if(dist[i]<=x)continue;//把边权大于二分答案的边建图 
        g[u[i]].push_back(v[i]);
        rd[v[i]]++;
    } 
    queue<int>q;int cnt=0;
    for(int i=1;i<=n;i++){if(rd[i]==0)q.push(i);} 
    while(!q.empty()){//拓扑排序
        int p=q.front();f[p]=++cnt;q.pop(); 
        for(int i=0;i<g[p].size();i++){
            int y=g[p][i];
            if(--rd[y]==0)q.push(y); 
        }    
    }
    if(cnt!=n)return 0;//判断是有环 
    ans.clear();//记录方案(即需要换方向的二类边)
    for(int i=1;i<=m;i++){
        if(dist[i]<=x&&f[u[i]]>f[v[i]])ans.push_back(i);
    }//                            如果从topo序大的点
    //                                指向topo序小的点则会产生环 
    //                             需要颠倒过来
    return 1;
} 
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u[i],&v[i],&dist[i]);//将边的信息用三元组存下来 
    } 
    int l=0,k,r=1e9+10,mid=(l+r)>>1;
    while(l<=r){          //二分颠倒过来的路径的最大值 
        mid=(l+r)>>1;     
        if(check(mid)){  //合法,记录答案 
            k=mid;//即最小的最大值
            r=mid-1;//这里不break是因为要找到跟小的k    
        }else l=mid+1;
    } 
    printf("%d %d\n",k,ans.size());
    for(int i=0;i<ans.size();i++){
        printf("%d ",ans[i]);
    }     
    printf("\n");
    return 0;
}
题解
Theme Jasmine by Kent Liao