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

    二分+dfs判环+拓扑序

    我们的目标是以最小的代价,反转边破坏所有的环

    首先可以发现题目中的$traffic$ $controllers$的数量为所反转边的最大权值

    那么所以边权小于等于$traffic$ $controllers$的数量的边是都可以反转的

    那么对于边权大于$traffic$ $controllers$的数量的边构成的图只要没有环,就满足条件了

    因为对于可以反转的边只要不能反转的边无环,总有方案可以使整张图无环

    那么对于已知$traffic$ $controllers$的数量,可以在$O(n)$的时间内判断是否可行

    因此可以将每条边排序,在二分最小$traffic$ $controllers$的数量

    那么前半部分完成了

    现在得到了以不可反转的边构成的图,求一种反转方案

    就对剩下的节点进行拓扑排序

    那么可以发现只要拓扑序大的点连向拓扑序小的节点,就一定我形成环

    因此将这种边反转即可

    所以总复杂度$O(nlogm+n+m)$

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN=100100;
    int n,m,first[MAXN],point[MAXN*2],len[MAXN*2],wh[MAXN*2];
    int nxt[MAXN*2],tot,vi[MAXN],si[MAXN],dfn[MAXN];
    bool bl;
    queue <int> q;
    vector <int> ans;
    struct node
    {
        int u,v,c,wh;
    }sh[MAXN];
    void add_edge(int x,int y,int l,int w)
    {
        tot++;
        nxt[tot]=first[x];
        first[x]=tot;
        point[tot]=y;
        len[tot]=l;
        wh[tot]=w;
    }
    bool cmp(node a,node b)
    {
        return a.c<b.c;
    }
    void dfs(int x,int MAX)
    {
        if (!bl)
          return;
        vi[x]=2;
        for (int i=first[x];i!=-1;i=nxt[i])
        {
            if (len[i]<=MAX)
              continue;
            if (vi[point[i]]==2)//表示有环
            {
                bl=0;
                break;
            }
            dfs(point[i],MAX);
        }
        vi[x]=1;
    }
    bool check(int mid)
    {
        int MAX=sh[mid].c;
        memset(vi,0,sizeof(vi));
        bl=1;
        for (int i=1;i<=n;i++)
        {
            if (!vi[i])//对于每一个节点进行遍历
            {
                dfs(i,MAX);
                if (!bl)
                  return 0;
            }
        }
        return 1;
    }
    int main()
    {
        memset(first,-1,sizeof(first));
        memset(nxt,-1,sizeof(nxt));
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&sh[i].u,&sh[i].v,&sh[i].c);
            sh[i].wh=i;
            add_edge(sh[i].u,sh[i].v,sh[i].c,i);
        }
        sort(sh+1,sh+1+m,cmp);
        int l,r;
        r=m;l=0;//二分枚举下标
        while (l<r)
        {
            int mid;
            mid=(l+r)/2;
            if (check(mid))
              r=mid;
            else
              l=mid+1;
        }
        int per;
        per=sh[r].c;
        if (per==0)
        {
            printf("%d %d
    ",per,0);
            return 0;
        }
        for (int i=1;i<=n;i++)
        {
            for (int j=first[i];j!=-1;j=nxt[j])
            {
                if (len[j]>per)
                {
                    si[point[j]]++;
                }
            }
        }
        for (int i=1;i<=n;i++)
        {
            if (si[i]==0)
            {
                q.push(i);
            }
        }
        int tot=0;
        while (!q.empty())
        {
            int f;
            f=q.front();
            q.pop();
            tot++;
            dfn[f]=tot;//记录拓扑序
            for (int i=first[f];i!=-1;i=nxt[i])
            {
                if (len[i]<=per)
                  continue;
                si[point[i]]--;
                if (si[point[i]]==0 && dfn[point[i]]==0)
                {
                    q.push(point[i]);
                }
            }
        }
        for (int i=1;i<=m;i++)
        {
            if (sh[i].c>per)
              break;
            if (dfn[sh[i].u]>dfn[sh[i].v])//反转边
              ans.push_back(sh[i].wh);
        }
        printf("%d %d
    ",per,(int)ans.size());
        for (int i=0;i<(int)ans.size();i++)
          printf("%d ",ans[i]);
        printf("
    ");
    }
  • 相关阅读:
    python读取 ini 配置文件
    Mysql 存储过程声明及使用
    PAT乙级1002
    Pat乙级1001
    第四届蓝桥杯省赛翻硬币
    蓝桥杯第四届省赛错误票据
    蓝桥杯第四届省赛前缀判断
    蓝桥杯第四届省赛第39级台阶
    蓝桥杯第四届省赛马虎的算式
    蓝桥杯第三届省赛试题取球游戏
  • 原文地址:https://www.cnblogs.com/huangchenyan/p/11228896.html
Copyright © 2011-2022 走看看