• codeforces 698B Fix a Tree


    http://www.codeforces.com/problemset/problem/698/B

    很不错的题目。不难但是需要一点思维和严谨。

    一个n条边的有向图,每个点都只有一条出边,要求修改最少点数的出边,使得整个图变成一个有根树(根节点的出边指向自己)。方案任意。n<=200000

    首先看这个图是什么样子的。

    首先可能有好几个连通块。

    对于每个连通块:有n个点n条边,所以把边看成无向边的话一定有一个环(自环或不是自环)。而且这个环一定是一个指向下一个,是一个强连通分量,

    不可能出现图二那种情况。

    这一个环上所有点的出度全都用上了,所以如果有其他点和它们连着的话,一定是指向这个环。

    所以这是一颗基环内向树。如下图1

     | 

               图1                                               图2

    找环的工作用tarjan就好了。找到所有环之后,就要构造一棵树了。

    我们的思路是先找到一个根,然后把所有其他连通块的环,易证需要且只需要取其中任意一个结点,将其指向根即可。

    首先,如果有自环的话,随意取一个当作根即可(因为由构造方法可知取哪一个并不影响最终改变的出边的数量)。

    否则,任取一个大环,任取其中一个结点作根即可,不过答案要加1.

    再将其余连通块按方法构造即可。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <string>
     4 #include <cstring>
     5 #include <algorithm>
     6 #include <cmath>
     7 #include <vector>
     8 using namespace std;
     9 
    10 const int N = 200010;
    11 int tot,top,n,color;
    12 int a[N],dfn[N],low[N],stack[N],c[N],size[N];
    13 bool v[N],flag[N];
    14 vector<int> cmem[N];
    15 
    16 void tarjan(int x)
    17 {
    18     low[x] = dfn[x] = ++tot;
    19     stack[++top] = x;
    20     v[x] = 1;
    21     int y = a[x];
    22     if(!dfn[y]) {
    23         tarjan(y);
    24         low[x] = min(low[x], low[y]);
    25     }
    26     else if(v[y])
    27         low[x] = min(low[x], dfn[y]);
    28         
    29     if(dfn[x]==low[x]) {
    30         color++;
    31         int temp;
    32         do {
    33             temp = stack[top--];
    34             c[temp] = color;
    35             v[temp] = 0;
    36             size[color]++;
    37             cmem[color].push_back(temp);
    38         } while(temp!=x);
    39     }
    40 }
    41 
    42 int main()
    43 {
    44     int i,root = 0,ans = 0;
    45     cin >> n;
    46     for(i = 1; i<=n; i++) {
    47         scanf("%d", &a[i]);
    48     }
    49     for(i = 1; i<=n; i++)
    50         if(!dfn[i]) tarjan(i);
    51     for(i = 1; i<=n; i++) {
    52         int y = a[i];
    53         if(c[y]!=c[i])
    54             flag[c[i]] = 1;
    55     }
    56     for(i = 1; i<=color; i++)
    57         if(!flag[i]) {
    58             if(size[i]==1) root = cmem[i][0];
    59         }
    60     if(!root) {
    61         ans++;
    62         for(i = 1; i<=color; i++)
    63             if(!flag[i]) {
    64                 root = cmem[i][0];
    65                 a[cmem[i][0]] = cmem[i][0];
    66                 break;
    67             }
    68     }
    69     for(i = 1; i<=color; i++)
    70         if(!flag[i] && cmem[i][0]!=root) {
    71             ans++;
    72             a[cmem[i][0]] = root;
    73         }
    74     printf("%d
    ", ans);
    75     for(i = 1; i<=n; i++)
    76         printf("%d ", a[i]);
    77     return 0;
    78 }
    View Code
  • 相关阅读:
    linux 学习(二)防火墙
    linux学习(一)开始
    ajax和sap以及网络安全
    仿苹果导航菜单js问题
    基本类型和引用类型调用是的区别(Object.create)
    箴言
    思维的宽度
    笔记
    循环传值_闭包
    一个问题的解法(兔子三个月之后每月都生兔子的问题)
  • 原文地址:https://www.cnblogs.com/liuaohan/p/5687534.html
Copyright © 2020-2023  润新知