zoukankan      html  css  js  c++  java
  • CF1100E Andrew and Taxi

    2020.11.3

    题目描述

    给定一个有向图,改变其中某些边的方向,它将成为一个有向无环图。现在求一个改变边方向的方案,使得所选边边权的最大值最小。

    解法

    容易发现对于一条边,把其删去和把其反向其实是等价的。若不翻转之前,这条边仅处于一个简单环里,那么反过来之后一定不会组成新环。若反过来之后组成了一个新的环,那么不看这条边原图里一定构成了另一个环(画图)。所以对于反向操作直接不走它即可。

    若选定了一条边其权值为 (v_i),那么权值小于等于 (v_i) 的边都可删可不删,就没有约束条件。若选最大的边,那么所有边都可删可不删,一定可以满足要求。而若选择最小的边,只有最小的边能删,约束条件很强。容易发现这是由单调性的,而边权限制在 (10^9) 内,可以直接进行二分了。而对于每次二分,不走权值小于等于 (mid) 的边,判断是否有环即可,可用 (tarjan) 或拓扑排序。

    对于第二问,需要求一种具体方案,只能用拓扑。先 (check) 一下 (ans),把最后的图建出来,此时一定是没有环的,那么这一定是一个 (DAG)。对它进行一遍拓扑,求出拓扑序。枚举原图中每条有向边 (u o v),若 (u) 的拓扑序比 (v) 的大,说明 (v) 要比 (u) 先被更新,所以这条边一定要反过来。

    #include<stdio.h>
    #include<string.h>
    #define N 200007
    #define M 300007
    
    inline int read(){
        int x=0; bool flag=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        return flag? x:-x;
    }
    
    struct E{
        int next,to;
    }e[M];
    int head[N],cnt=0;
    
    inline void add(int id,int to){
        e[++cnt]=(E){head[id],to};
        head[id]=cnt;
    }
    
    int X[M],Y[M],D[M];
    int n,m,dep[N],in[N],sta[N],top=0;
    bool check(int lim){
        memset(in,0,sizeof(in));
        memset(head,0,sizeof(head));
        cnt=0,top=0;
        int timer=0;
        for(int i=1;i<=m;i++)
            if(D[i]>lim) add(X[i],Y[i]),in[Y[i]]++;
        for(int i=1;i<=n;i++)
            if(!in[i]) sta[++top]=i,dep[i]=++timer;
        while(top){
            int u=sta[top--];
            for(int i=head[u];i;i=e[i].next){
                int v=e[i].to;
                if(--in[v]) continue;
                sta[++top]=v,dep[v]=++timer;
            }
        }
        for(int i=1;i<=n;i++)
            if(in[i]) return 0;
        return 1;
    }
    
    int main(){
    //    freopen("pestc.in","r",stdin);
    //    freopen("pestc.out","w",stdout);
        n=read(),m=read();
        int l=0,r=0,ans;
        for(int i=1;i<=m;i++){
            X[i]=read(),Y[i]=read(),D[i]=read();
            if(D[i]>r) r=D[i];
        }
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%d",ans);
        check(ans);
        int ret=0;
        for(int i=1;i<=m;i++)
            if(D[i]<=ans&&dep[X[i]]>dep[Y[i]]) ret++;
        printf(" %d
    ",ret);
        for(int i=1;i<=m;i++)
            if(D[i]<=ans&&dep[X[i]]>dep[Y[i]])
                printf("%d ",i);
    } 
    
  • 相关阅读:
    World Wind Java开发之一(转)
    Excel如何显示隐藏列?
    Oracle开发›如何取出每个分组的第一条记
    如何解决EXCEL中的科学计数法
    使用POI 读取 Excel 文件,读取手机号码 变成 1.3471022771E10
    POI读取单元格信息及单元格公式
    java中判断字符串是否为数字的方法的几种方法
    阿里云OSS的 存储包、下行流量包、回流流量包 三者有啥关系
    vue+datatable+vue-resource动态获取jsonp数据2
    vue+datatable+vue-resource动态获取jsonp数据
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/13924376.html
Copyright © 2011-2022 走看看