zoukankan      html  css  js  c++  java
  • HDU 5409 CRB and Graph

    题意:

    给出一个无向图。

    问删去每一条边后,是否出现一对(u,v) st 删去这条边后,u和v不连通,且u<v,若有多对,输出u最大的,然后v最小的。

    数据范围10^5

    思路:

    首先显然先用tarjan处理处各个强连通块。顺便处理处每个块中的点编号最大的是多少。以belong[x]表示x这个点属于哪个块。

    对于一条边,若他不是桥边,则删去他后不会出现不联通的u,v,结果是0 0

    如果是桥边,就以(belong[x], belong[y])为边加入新图中

    然后我们缩完了点获得了一棵树.

    若是桥边:太(wo)巧(tai)妙(chun)惹。

      首先达成共识:若i这个块中,最大的点为maxn[i],则点maxn[i]+1和不在i这个块中。

            同理若以maxn[i]表示i子树上的最大值,则点maxn[i]+1不在这i子树上

      然后处理:

      1.以n所在的块为根,处理这棵树。接下来都把块视为点了因为打字麻烦= =

      2.处理出maxn[i]为i的子树中值得最大值 即 maxn[i] = max{maxn[j] | i 是 j 的祖先},这个可以简单树形DP处理出。顺便处理出每个点的深度。

      3.接下来继续前面的枚举原图的每一条边。对于边两边的点:

        (1)属于同一个块,答案如上面说的

        (2)属于不同块。则设他们所在的块为u, v。可以知道在刚才那棵树中,u一定是v的父亲或者v一定是u的父亲(树上的边直接连接了他们),为了表达方便我们设u是父亲。删去边后变成了v子树以及除了v子树以外的一块。因为有maxn[v] <= maxn[u],且有maxn[v] + 1不在v子树上,所以maxn[v] +1一定在另一块上,也就是他们分开了,那么这是一组可行解。怎么证明是最大呢= =因为maxn[v]是v子树上最大值,没有别的值比他大了。。且不会选择出maxn[v] = n因为v一定是儿子,而n在整棵树的根上,他所在的块一定不是儿子。

    大概说的挺清楚了吧,,啰嗦患者的日常= =

    其他看代码吧,虽然注释也没多少= = 然后minn是没用的数组哈哈哈= =

      1 #include <cstring>
      2 #include <cstdio>
      3 #include <algorithm>
      4 using namespace std;
      5 const int N = 100005;
      6 
      7 int head[N], maxn[N], minn[N], dfn[N], low[N], st[N], belong[N];
      8 int deep[N];
      9 
     10 struct point{
     11     int u, v, next;
     12     point(){};
     13     point(int x, int y, int z){
     14         u=x, v = y, next = z;
     15     }
     16 }p[N<<1];
     17 int no, top, num, id;
     18 struct Ans{
     19     int x, y;
     20     Ans(){};
     21     Ans(int _u, int _v){
     22         x = _u; y = _v;
     23     }
     24 }ans[N];
     25 void init(){
     26     memset(head, -1, sizeof(head));
     27     memset(dfn, -1, sizeof(dfn));
     28     memset(maxn, 0, sizeof(maxn));
     29     memset(minn, 0x3f, sizeof(minn));
     30     no = id = num = top = 0;
     31 }
     32 
     33 void add(int x, int y){
     34     p[no] = point(x, y, head[x]);
     35     head[x] = no++;
     36     p[no] = point(y, x, head[y]);   head[y] =no++;
     37 }
     38 //tarjan求强连通,belong[i]表示属于哪个块
     39 //记录每个块中编号最大值
     40 void tarjan( int x, int fa){
     41     int cnt = 0;
     42     low[x] = dfn[x] = ++num;
     43     st[++top] = x;
     44     int i, y;
     45     for(i = head[x]; i != -1; i = p[i].next){
     46         y = p[i].v;
     47         if(y == fa)continue;
     48         if(dfn[y] == -1)    {
     49             tarjan(y, x);
     50             low[x] = min(low[x], low[y]);
     51         }
     52         else low[x] = min(low[x], dfn[y]);
     53     }
     54     if(dfn[x] == low[x]){
     55         id ++;
     56         do{
     57             y = st[top--];
     58             belong[y] = id;
     59             maxn[id] = max(maxn[id], y);
     60             minn[id] = min(minn[id], y);
     61         }while(x != y);
     62     }
     63 }
     64 //maxn表示i 及其子树的最大值
     65 void dfs(int x, int fa){
     66     int i, y;
     67     for(i = head[x]; i != -1; i = p[i].next){
     68         y = p[i].v;
     69         if(y == fa) continue;
     70         deep[y] = deep[x] + 1;
     71         dfs(y, x);
     72         maxn[x] = max(maxn[x], maxn[y]);
     73     }
     74 
     75 }
     76 int main(){
     77     int TC, n, m, i, x, y, u, v;
     78     scanf("%d", &TC);
     79     while(TC--){
     80         scanf("%d%d", &n, &m);
     81         init();
     82         for(i = 1; i <= m; i++){
     83             scanf("%d%d", &x, &y);
     84             add(x, y);
     85         }
     86         tarjan(1, 1);
     87 
     88         m = no;
     89         no = 0;
     90         memset(head, -1, sizeof(head));
     91         //重新建出树
     92         for(i = 0; i < m; i+= 2){
     93             x = belong[p[i].u]; y = belong[p[i].v];
     94             //属于同一个块,非桥边,删了之后不出现不通的点
     95             if(x == y) ans[i/2] = Ans(0, 0);
     96             else {
     97                 ans[i/2] = Ans(x, y);
     98                 add(x, y);
     99             }
    100         }
    101         deep[1] = 0;
    102         //以n所在的块为根,dfs这棵树,求出子树的maxn
    103         dfs(belong[n], 0);
    104         m >>= 1;
    105         for(i = 0; i < m; i++){
    106             if(ans[i].x == 0)   printf("0 0
    ");
    107             else{
    108                 //因为只隔一条边
    109                 u = ans[i].x;   v  = ans[i].y;
    110                 if(deep[u] > deep[v]){
    111                     printf("%d %d
    ", maxn[u], maxn[u] + 1);
    112                 }   else {
    113                     printf("%d %d
    ", maxn[v], maxn[v] + 1);
    114                 }
    115             }
    116         }
    117     }
    118     return 0;
    119 }
  • 相关阅读:
    二叉树非递归先中后序遍历 及 非递归交换二叉树两个孩子的位置
    COM 学习小记录
    Linux 信号量 生产者消费者小例题
    打印数字 形状有点得味
    (链表)链表倒序
    C++ 数组名作为函数参数 都是我的错
    进程 线程
    《C++ Qt 设计模式》8|15拼图 小游戏的简单实现。拜托,别乱点!
    PMAC运动程序例程(一)
    中国获得2022年冬奥会举办权【经济学人】
  • 原文地址:https://www.cnblogs.com/bbbbbq/p/4746621.html
Copyright © 2011-2022 走看看