• HDU 4297 One and One Story [有向带环图LCA]


      给一个有向图,每个顶点的出度为1,A和B是图中的两个顶点,A和B每次只能走一步或者不走,设他们相遇时A走了X步,B走了Y步,使max(x,y)最大,有多解时,使min(x,y)最小,如果还有多解,使x>=y。

      其实这个图最特殊的地方就是它每个顶点的出度仅为1。我们都知道树的特性是除了根节点外每个顶点的入度为1,那我们把这个图全部建反向边,也就是说每个顶点的入度为1。但是这个图是有环的,它没有入度为0的根,如果我们把环缩成一个点的话,就会发现这个环的整体入度为0,因为每个点在构环的时候入度就已经饱和了,同样,我们也很容易证明每个连通块至多只有一个环,因为一个连通块不可能有两个入度为0的点,这样建反向边并且缩环成点后,原图就变成了一棵树,原题就成了树上的LCA问题。

      写起来还是比较麻烦的,要预处理出每个环的长度,一个环上两点的相对距离,以及每个非环点进入环的第一个点。同时,为了方便,建一个虚拟节点0,向每个缩环的点引一条边。对于每次查询,如果LCA是虚拟节点,显然不连通,如果LCA是普通树节点,也可直接求解,最麻烦的情况就是进入了一个环,这时候肯定会让其中的一个人走,另一个人停下等待,因为他们两之间是一个追及的关系,如果都走那就永远遇不到了。。。讨论一下谁走满足题目要求即可。。

      1 #pragma comment(linker, "/STACK:1024000000,1024000000")
      2 #include <stdio.h>
      3 #include <string.h>
      4 #include <math.h>
      5 #include <algorithm>
      6 #define MAXN 500005
      7 using namespace std;
      8 
      9 struct edge{
     10     int v,e,n;
     11 }e[MAXN];
     12 int n, q, tu, tv, fa[MAXN], next[MAXN], p[MAXN];
     13 int first[MAXN], es;
     14 void addedge(int u,int v) {
     15     e[es].v = v, e[es].n = first[u], first[u] = es++;
     16 }
     17 //LCA+RMQ
     18 struct lcast{int dep, id;}list[MAXN*2], r[30][MAXN];
     19 int mm[MAXN*2], pos[MAXN], dis[MAXN], rid;
     20 void dfs(int u, int dep) {
     21     pos[u] = ++rid, list[rid].id = u, list[rid].dep = dep;
     22     dis[u] = dep;
     23     for (int i = first[u]; i != -1; i = e[i].n) {
     24         dfs(e[i].v, dep + 1);
     25         ++rid, list[rid].id = u, list[rid].dep = dep;
     26     }
     27 }
     28 void makermq() {
     29     rid = 0;
     30     dfs(0, 0);
     31     mm[1] = 0, r[0][1] = list[1];
     32     for(int i = 2; i <= rid; i ++) {
     33         mm[i] = (i&i-1) ? mm[i-1] : mm[i-1]+1;
     34         r[0][i] = list[i];
     35     }
     36     for (int i = 0; i < mm[rid]; i ++)
     37         for (int j = 1; j+(1<<i+1)-1 <= rid; j ++)
     38             r[i+1][j] = r[i][j].dep < r[i][j+(1<<i)].dep ? r[i][j] : r[i][j+(1<<i)];
     39 }
     40 int lca(int x, int y) {
     41     if(pos[x] > pos[y]) return lca(y, x);
     42     x = pos[x], y = pos[y];
     43     int k = mm[y - x + 1];
     44     y -= (1 << k) - 1;
     45     return r[k][x].dep < r[k][y].dep ? r[k][x].id : r[k][y].id;
     46 }
     47 //缩点构图
     48 int id[MAXN], cid[MAXN], cids, ids;
     49 //cirn 该环上点的个数,cirdis,环上两点的相对距离,cirin,进入环中的点
     50 int cirn[MAXN], cirdis[MAXN], cirin[MAXN];
     51 int dfsin(int u) {
     52     return cirin[u] ? cirin[u] : cirin[u] = dfsin(fa[u]);
     53 }
     54 void makegraph() {
     55     memset(cirn, 0 ,sizeof cirn);
     56     memset(cirin, 0, sizeof cirin);
     57     memset(id, 0, sizeof id); ids = cids = 0;
     58     for (int i = 1, j; i <= n; i++) {
     59         if (id[i]) continue;
     60         int sid = ids;
     61         for (j = i; !id[j]; j = fa[j]) id[j] = ++ids;
     62         //如果遇到的节点标记是这次一路找过来的,说明生成了一个环
     63         if (id[j] > sid || fa[i] == i) {
     64             //将该环中的点ID统一,并统计环中点的个数,以及环上两点的相对距离
     65             int cd = 1;
     66             ids = id[j], cid[++cids] = id[j], cirn[ids] = 1, cirdis[j] = cd, cirin[j] = j;
     67             for (int k = fa[j]; k != j; k = fa[k])
     68                 id[k] = ids, cirn[ids]++, cirdis[k] = ++cd, cirin[k] = k;
     69         }
     70     }
     71     //处理非环点进入环中的第一个点
     72     for (int i = 1; i <= n; i++) {
     73         if (cirin[i]) continue;
     74         else cirin[i] = dfsin(i);
     75     }
     76     memset(first, -1, sizeof first); es=0;
     77     for (int i = 1; i <= n; i++)
     78         if(id[i] != id[fa[i]]) addedge(id[fa[i]], id[i]);
     79     for (int i = 1; i <= cids; i++)
     80         addedge(0, cid[i]);
     81     makermq();
     82 }
     83 int main() {
     84     //freopen("test.in", "r", stdin);
     85     while (scanf("%d%d", &n, &q) != EOF) {
     86         for (int i = 1; i <= n; i++) p[i] = i;
     87         for (int i = 1; i <= n; i++)
     88             scanf("%d", &tu), fa[i] = tu;
     89         makegraph();
     90         while (q-- ) {
     91             scanf("%d%d", &tu, &tv);
     92             int lc = lca (id[tu],id[tv]);
     93             //LCA是虚拟根节点,说明不在一颗子树上
     94             if(lc == 0) printf("-1 -1\n");
     95             //LCA不是环,一般的LCA问题
     96             else if (cirn[lc] == 0){
     97                 printf("%d %d\n", dis[id[tu]] - dis[lc], dis[id[tv]] - dis[lc]);
     98             } else {
     99                 //uin和vin是u和v进入环的顶点,disu和disv是到环距离,det1和det2是u和v在环上要走的距离
    100                 int uin = cirin[tu], vin = cirin[tv];
    101                 int disu = dis[id[tu]] - dis[lc], disv = dis[id[tv]] - dis[lc];
    102                 int det1 = (cirdis[vin] - cirdis[uin] + cirn[id[uin]]) % cirn[id[uin]];
    103                 int det2 = (cirn[id[uin]] - cirdis[vin] + cirdis[uin]) % cirn[id[uin]];
    104                 //保证解满足题意,最大值最小,然后是最小值最大,然后是A>=B
    105                 int ansu = disu + det1, ansv = disv;
    106                 if (max(disu+det1, disv) > max(disu, disv+det2))ansu = disu, ansv = disv+det2;
    107                 else if(max(disu+det1, disv) < max(disu, disv+det2))ansu = disu+det1, ansv = disv;
    108                 else if(min(disu+det1, disv) < min(disu, disv+det2))ansu = disu+det1, ansv = disv;
    109                 else if(min(disu+det1, disv) > min(disu, disv+det2))ansu = disu, ansv = disv+det2;
    110                 printf("%d %d\n", ansu, ansv);
    111             }
    112         }
    113     }
    114     return 0;
    115 }
  • 相关阅读:
    Delphi数据库处理
    delphi 递归的方法
    C#中StringBuilder用法以及和String的区别
    算法导论12.2节习题解答
    算法导论9.11习题解答(二叉树)
    算法导论10.15习题解答(deque实现源码)
    算法导论9.39习题解答(寻找中位数)
    算法导论84习题解答
    算法导论10.17习题解答(用两个队列实现一个栈)
    算法导论10.27习题解答(单链表逆转)
  • 原文地址:https://www.cnblogs.com/swm8023/p/2694777.html
Copyright © 2020-2023  润新知