题目大意:
就是现在有一个有向图, 每个点出度都是1(只有一条出边), 这个图可以有环, 也可以有自环, 图的点的个数 N <= 500000
然后有K次询问 (K <= 500000), 每次询问给出两个整数u和v表示男主在u房间, 女主在v房间, 现在两个人要走到同一个房间, 需要走多少步
如果不可能到同一个房间, 那么输出-1 -1, 否则如果男主需要走A步女主需要走B步, 那么输出max(A, B)最小的方案, 如果有多种输出min(A, B)最小的方案, 如果依旧有多种, 输出B比较小的方案
大致思路:
首先这个题要注意到图可能被分成多个连通图, 如果男女主在不同的连通部分的话, 是不可能相遇的, 这个连通的性质可以用并查集来维护
如果男女主在同一个连通的块上, 那么考虑到每个点出度都是1, 这个部分最多只有一个环从这个环上不会有其他的出边, 只会有入边, 那么就是一个环周围有很多分支的树, 换上的点是这些树的根节点, 于是由于树上的点一定都能走到环上, 而环上的点都是连通的, 于是男女主一定可以相遇
那么如果男女猪脚都在环上, 我们在找出环的同时给环上的点标号, 就可以轻松找出环上点的最近距离(可以想到此时男女主角一定是一个人站着不动另外一个去找这个人, 两个人走的路的max最小)
如果男女有一个在树上一个在环上, 那么需要先从树走到环上, 然后就和两个人在环上一样的道理了
如果男女都在树上但不是同一棵子树(就是环上同一个点分出来的分支), 就需要都走到环上, 接下来和上面一样了
如果两个人都在同一个子树上, 两个人当然是走到他们的最近公共祖先, 于是用ST表来维护一下就行了
考虑到所有的子树的结点树总和不超过50W, 将这个森林的树的ST表写成一个就行, 不用担心内存问题
代码注意一下细节就好了, 其实不是很难, 就是要注意分类讨论就行
代码如下:
Result : Accepted Memory : 39008 KB Time : 1107 ms
/* * Author: Gatevin * Created Time: 2015/7/31 14:04:11 * File Name: Sakura_Chiyo.cpp */ #pragma comment(linker, "/STACK:16777216") #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; #define maxn 500010 int nex[maxn]; int rootLoop[maxn]; //---------------LCA vector<int> G[maxn]; int st[2*maxn + 1][20]; int fa[maxn], dfn[maxn], pos[maxn]; int sum; void add(int x) { st[++sum][0] = x; pos[x] = sum; return; } void dfs(int now, int ro) { for(int i = 0, sz = G[now].size(); i < sz; i++) { int u = G[now][i]; if(u == fa[now]) continue; dfn[u] = dfn[now] + 1; fa[u] = now; add(now); dfs(u, ro); } rootLoop[now] = ro;//指向环上的根, 而dfn代表代打环上的根的距离 add(now); return; } int Min(int u, int v) { return dfn[u] < dfn[v] ? u : v; } int lca(int u, int v) { u = pos[u], v = pos[v]; if(u > v) swap(u, v); int t = log(v - u + 1.) / log(2.); return Min(st[u][t], st[v - (1 << t) + 1][t]); } //---------------- int du[maxn]; int father[maxn]; int vis[maxn]; int loop[maxn]; int loopLength[maxn]; int find(int x) { return x == father[x] ? x : father[x] = find(father[x]); } void Union(int x, int y) { int fx = find(x); int fy = find(y); if(fx != fy) father[fx] = fy; return; } int N, K; vector <int> root; void init() { memset(rootLoop, -1, sizeof(rootLoop)); memset(vis, 0, sizeof(vis)); for(int i = 1; i <= N; i++) G[i].clear(); root.clear(); for(int i = 1; i <= N; i++) { if(loop[i] && loop[nex[i]]) continue;//环上的边 G[nex[i]].push_back(i); if(loop[nex[i]] && !vis[nex[i]]) { root.push_back(nex[i]);//子树根节点 vis[nex[i]] = 1; } } //for(int i = 0, sz = root.size(); i < sz; i++) // cout<<root[i]<<" "; //cout<<endl; //cout<<"ttt"<<endl; sum = 0; for(int i = 0, sz = root.size(); i < sz; i++) dfn[root[i]] = 0, fa[root[i]] = -1, dfs(root[i], root[i]); //cout<<"sdsds"<<endl; for(int j = 1; (1 << j) <= sum; j++) for(int i = 1; i <= sum - (1 << j) + 1; i++) st[i][j] = Min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); return; } void answer(int x1, int y1, int x2, int y2) { if(max(x1, y1) < max(x2, y2)) { printf("%d %d\n", x1, y1); return; } if(max(x1, y1) > max(x2, y2)) { printf("%d %d\n", x2, y2); return; } if(min(x1, y1) < min(x2, y2)) { printf("%d %d\n", x1, y1); return; } if(min(x1, y1) > min(x2, y2)) { printf("%d %d\n", x2, y2); return; } if(y1 > y2) printf("%d %d\n", x2, y2); else printf("%d %d\n", x1, y1); return; } int main() { //freopen("data.in", "r", stdin); while(~scanf("%d %d", &N, &K)) { memset(du, 0, sizeof(du)); for(int i = 0; i <= N; i++) father[i] = i; for(int i = 1; i <= N; i++) { int tmp; scanf("%d", &tmp); Union(i, tmp);//这两个是连在一起的, Union多个分离的图 nex[i] = tmp;//i的唯一出口 du[tmp]++;//入度+1 } memset(vis, 0, sizeof(vis)); memset(loop, 0, sizeof(loop)); memset(loopLength, 0, sizeof(loopLength)); for(int i = 1; i <= N; i++) if(!vis[find(i)]) { //从i开始向前找环 //vis[find(i)] = 1; int now = i; vis[now] = 1; while(!vis[nex[now]]) { now = nex[now]; vis[now] = 1; }//接下来nex[now]走过了 int nn = nex[now]; int cnt = 1; if(nn == nex[nn]) { loop[nn] = 1;//一个点的自环 loopLength[nn] = 1; } else { while(nn != now)//多个点的环 { loop[nn] = cnt++; nn = nex[nn]; } loop[nn] = cnt++; loopLength[now] = cnt - 1; nn = nex[now]; while(nn != now) { loopLength[nn] = cnt - 1; nn = nex[nn]; }//再转一圈确定环的大小 } vis[find(i)] = 1; } //for(int i = 1; i <= N; i++) // if(loop[i]) cout<<i<<" "; //cout<<endl; //cout<<"hehe"<<endl;; //以上找到了所有的环, 用loop[v]非零标记了他们 //接下来处理LCA以及每个分支的起始环上的父亲 init(); int x, y; while(K--) { scanf("%d %d", &x, &y); if(find(x) != find(y))//不在同一个连通上 { puts("-1 -1"); continue; } if(loop[x] && loop[y])//都在环上 { if(loop[x] < loop[y]) { int xy = loop[y] - loop[x]; int yx = loopLength[x] - xy; if(xy <= yx) printf("%d 0\n", xy); else printf("0 %d\n", yx); continue; } else { int yx = loop[x] - loop[y]; int xy = loopLength[x] - yx; if(yx < xy) printf("0 %d\n", yx); else printf("%d 0\n", xy); continue; } } if(loop[x] && !loop[y])//x在环上, y在枝上 { int ly = rootLoop[y];//y的根在环上的点 int dy = dfn[y];//y到环上的距离 if(loop[x] < loop[ly]) { int xy = loop[ly] - loop[x]; int yx = loopLength[x] - xy; answer(xy, dy, 0, dy + yx);//前面一个和后面一个选择一种 continue; } else { int yx = loop[x] - loop[ly]; int xy = loopLength[x] - yx; answer(xy, dy, 0, dy + yx); continue; } } if(loop[y] && !loop[x])//y在环上, x在枝上 { int lx = rootLoop[x]; int dx = dfn[x]; if(loop[lx] < loop[y]) { int xy = loop[y] - loop[lx]; int yx = loopLength[lx] - xy; answer(xy + dx, 0, dx, yx); continue; } else { int yx = loop[lx] - loop[y]; int xy = loopLength[lx] - yx; answer(xy + dx, 0, dx, yx); continue; } } if(!loop[x] && !loop[y])//都在枝上 { if(rootLoop[x] == rootLoop[y])//在同一棵树上, 找LCA { int z = lca(x, y); int dx = -dfn[z] + dfn[x]; int dy = -dfn[z] + dfn[y]; printf("%d %d\n", dx, dy); continue; } //不在一棵树上求环上点的距离 int lx = rootLoop[x]; int ly = rootLoop[y]; int dx = dfn[x], dy = dfn[y]; if(loop[lx] < loop[ly]) { int xy = loop[ly] - loop[lx]; int yx = loopLength[lx] - xy; answer(xy + dx, dy, dx, yx + dy); continue; } else { int yx = loop[lx] - loop[ly]; int xy = loopLength[lx] - yx; answer(xy + dx, dy, dx, dy + yx); continue; } } } } return 0; }