One and One Story
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 327680/327680 K (Java/Others)
Total Submission(s): 760 Accepted Submission(s): 353
You, as a member of the FFF Inquisition, 2 are fed up with such game since you believe that to make things fair, you should not keep providing guidance information while risking remaining forever alone 3 . So you decided to write a program working out guidance for these sweet small lovers on behalf of you. ( Another reason is, you have to help K couples, which would make you somewhat overwhelmed. )
Fortunately, you are to handle not the Flash game above, but a simplified version: In the game, a maze consists of some rooms connected with one-way hallways. For each room, there is exactly one outgoing hallway here, and it would lead directly to some room (not necessarily a different one). The boy and girl are trapped in (not necessarily different) rooms. In each round of the games, both of them could choose to stay in the current room or walk to the room to which the unique outgoing hallway leads. Note that boy and girl could act independently of each other. Your goal is to come to the reunion between them.
Your program should determine a pair of numbers (A,B) for each couple of boy and girl, where A represents number of hallway the boy walked through, B the girl, that could lead reunion between them. First, your program should minimize max(A,B). If there are several solutions, you should then guarantee that min(A,B) is minimized subject to above. If, satisfying above conditions, there are still multiple solutions, girl should walk less, that is you should then keep A >= B subject to conditions above.
In case they could not reunion, just let A = B = -1.
---------------------------------------------------------------------------------
1It’s available here: http://armorgames.com/play/12409/one-and-one-story
2See http://bakatotest.wikia.com/wiki/FFF_Inquisition
3http://foreveralonecomic.com/
In each test case, in the first line there are two positive integers N and K (1 <= N <= 500000; 1 <= K <= 500000) denoting the number of rooms and number of couples. Rooms a numbered from 1 to N.
The second line contains n positive integers: the i th integer denotes the number of room to which the hallway going out of room i leads.
The following K lines are queries of several couples. Each query consists of two positive integers in a single line denoting the numbers of rooms where the lovers currently are: first the boy, then the girl.
Please process until EOF (End Of File).
See samples for detailed information.
12 5 4 3 5 5 1 1 12 12 9 9 7 1 7 2 8 11 1 2 9 10 10 5 12 5 4 3 5 5 1 1 12 12 9 9 7 1 7 2 8 11 1 2 9 10 10 5
2 3 1 2 2 2 0 1 -1 -1 2 3 1 2 2 2 0 1 -1 -1
题意:有n个房间,第i个房间能直接到房间a[i],每一次你能选择到房间a[i]或者是停留不走。给出询问u,v,问他们能不能在一起,在一起的话,最少要多少步,如果有多中方案的话,输出max(a,b)更小的,如果还是有一样的话,输出a>=b的。
思路:。。。由于每个点出度为1,入读不确定,也就是说一个点最多只在一个环里面,一个环可能有多个"入口“,进了环之后,只能在这个环里面绕圈,出不去。 那么我们分情况讨论。
第一种:两个人不在一个连通分量里面,不可能在一起。这个我们能用并查集判断。
第二种:两个人都在环中,显然最优的方案是,一个人不动,另外一个人绕圈去找另一个人。选择两个人不动中最优的。
第三种:一个人在环中,一个人在环外,那么显然在环外的人肯定要先走到环内。然后就变成了第二种情况了。
第四种:两个人都在环外,这时候又要分两种情况。1:他们拥有同样的环的入口,表明他们不需要进入环就能见面了。这时候找到他们的LCA就行了。 2:他们进入同一个环,但是入口不同,那么就让他们先走到环,然后变成第二种情况。
思路可能不难想,但是编程的时候会有诸多的细节要注意。容易写错。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<vector>
using namespace std;
const int maxn = 500000 + 5;
#pragma comment (linker,"/STACK:102400000,102400000")
int n, Q;
int * to;
int * size;
int ** anc;
int * father;
int * p;
int * L;
bool * vis;
int * S;
int c;
int find(int x)
{
if (x == p[x]) return x;
return p[x] = find(p[x]);
}
inline int max(int a, int b) { return a > b ? a : b; }
inline int min(int a, int b) { return a < b ? a : b; }
void init()
{
father = new int[n + 5];
to = new int[n + 5], size = new int[n + 5];
p = new int[n + 5];
L = new int[n + 5];
S = new int[n + 5];
anc = new int*[n + 5];
vis = new bool[n + 5];
for (int i = 1; i <= n; ++i) p[i] = i, size[i] = 0, L[i] = 0, vis[i] = false, father[i] = -1;
}
void release()
{
delete[] to; delete[]p; delete[]L;
delete[] size;
for (int i = 0; i < n + 5; ++i) delete[]anc[i];
delete[] anc;
}
void dfs(int u)
{
c = 0;
S[c++] = u;
while (c>0) {
u = S[c - 1];
if (vis[u]) {
bool flag = false;
int fa = find(u);
for (int i = 0; i < c - 1; ++i) {
if (S[i] == u) flag = true;
if (!flag) father[S[i]] = S[i + 1];
if (flag) ++size[fa];
p[S[i]] = fa;
}
for (int i = c - 2; i >= 0; --i)
L[S[i]] = L[S[i + 1]] + 1;
return;
}
vis[u] = true;
S[c++] = to[u];
}
}
void input()
{
init();
for (int i = 1; i <= n; ++i) scanf("%d", to + i);
for (int i = 1; i <= n;++i)
if (!vis[i]) { c = 0; dfs(i); }
delete[] vis;
delete[] S;
delete[] to;
int maxL = -1;
for (int i = 1; i <= n;++i)
if (L[i] > maxL) maxL = L[i];
int k = 0;
for (k = 0; (1 << k) <= maxL; ++k);
for (int i = 1; i <= n; ++i)
{
anc[i] = new int[k];
anc[i][0] = father[i];
for (int j = 1; j < k; ++j) anc[i][j] = -1;
}
delete[] father;
for (int j = 1; j < k;++j)
for (int i = 1; i <= n; ++i)
{
int a = anc[i][j - 1];
if (a == -1) continue;
anc[i][j] = anc[a][j - 1];
}
}
void Update(int a1, int b1, int a2, int b2, int&A, int &B)
{
if (max(a1, b1)<max(a2, b2)) A = a1, B = b1;
else if (max(a1, b1)>max(a2, b2)) A = a2, B = b2;
else if (min(a1, b1) < min(a2, b2)) A = a1, B = b1;
else if (min(a1, b1)>min(a2, b2)) A = a2, B = b2;
else if (a1 >= b1) A = a1, B = b1;
else A = a2, B = b2;
}
int find_rt(int u)
{
int k = 0;
for (k = 0; (1 << k) <= L[u]; ++k); --k;
for (int i = k; i >= 0;--i)
if (anc[u][i] != -1) u = anc[u][i];
if (anc[u][0] != -1) u = anc[u][0];
return u;
}
void lca(int u, int v,int &a,int&b)
{
int maxL = max(L[u], L[v]);
int k = 0;
for (k = 0; (1 << k) <= maxL; ++k); --k;
if (L[u] < L[v]) {
for (int j = k; j >= 0; --j)
if (L[v] - (1 << j) >= L[u]) v = anc[v][j];
}
else if (L[u]>L[v]) {
for (int j = k; j >= 0;--j)
if (L[u] - (1 << j) >= L[v]) u = anc[u][j];
}
a = u, b = v;
if (a == b) return;
for (int j = k; j >= 0;--j)
if (anc[u][j] != -1 && anc[u][j] != anc[v][j])
u = anc[u][j], v = anc[v][j];
a = u, b = v;
if (anc[u][0] != -1) a = anc[a][0];
if (anc[v][0] != -1) b = anc[b][0];
}
void solve()
{
while (Q--) {
int A, B;
int a, b; scanf("%d%d", &a, &b);
//case 1: 不同环
if (find(a) != find(b)) A = B = -1;
//case 2: 同在环中
else if (anc[a][0] == -1 && anc[b][0] == -1)
{
int diff = L[a] - L[b];
if (diff < 0) diff = size[find(a)] + diff;
if (diff <= size[find(a)] - diff) A = diff, B = 0;
else A = 0, B = size[find(a)] - diff;
}
//case 3: 都不在环上
else if (anc[a][0] != -1 && anc[b][0] != -1)
{
int rta = find_rt(a), rtb = find_rt(b);
if(rta==rtb) lca(a, b, rta, rtb);
int d1 = L[a] - L[rta], d2 = L[b] - L[rtb];
a = rta, b = rtb;
if (rta == rtb) A = d1, B = d2;
else {
int diff = L[a] - L[b];
if (diff < 0) diff = size[find(a)]+diff;
int a1 = d1 + diff, b1 = d2, a2 = d1, b2 = d2 + size[find(b)] - diff;
Update(a1, b1, a2, b2, A, B);
}
}
//case 4:一个环内,一个环外
else if (anc[a][0] == -1 && anc[b][0] != -1)
{
int rt = find_rt(b);
int d = L[b] - L[rt];
b = rt;
int diff = L[a] - L[b];
if (diff < 0) diff = size[find(a)] + diff;
int a1 = diff, b1 = d, a2 = 0, b2 = d + size[find(a)] - diff;
Update(a1, b1, a2, b2, A, B);
}
else if (anc[a][0] != -1 && anc[b][0] == -1)
{
int rt = find_rt(a);
int d = L[a] - L[rt];
a = rt;
int diff = L[a] - L[b];
if (diff < 0) diff = size[find(a)] + diff;
int a1 = d + diff, b1 = 0, a2 = d, b2 = size[find(b)] - diff;
Update(a1, b1, a2, b2, A, B);
}
printf("%d %d\n", A, B);
}
}
int main()
{
while (scanf("%d%d", &n,&Q) == 2)
{
input();
solve();
}
}