给一个有向图,每个顶点的出度为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 }