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\n",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\n",per,(int)ans.size());
    for (int i=0;i<(int)ans.size();i++)
      printf("%d ",ans[i]);
    printf("\n");
}

 

posted @ 2019-07-22 22:21  SevenDawns  阅读(225)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end