题目大意:
就是现在有一个有向图, 每个点出度都是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;
}