Hackerrank 101 Hack Jan 2016 Intersecting Paths
editorialを見た。これはまだ解くには厳しい。
Intersecting Paths
解法
以下が成り立つのでdoublingする。 $x,y$のpathが交差 $\Leftrightarrow$ $x,y$のpathの終端が同じあるいは$x$のpathが$y$を通る。
成り立つことの証明(editorialにある)はちゃんと追えていない。
実装
#include <iostream>
#include <vector>
#include <stack>
#include <cmath>
#define repeat(i,n) for (int i = 0; (i) < (n); ++(i))
#define repeat_reverse(i,n) for (int i = (n)-1; (i) >= 0; --(i))
using namespace std;
int main() {
// input
int n; cin >> n;
vector<int> a(n); repeat (i,n) cin >> a[i];
// make single step graph
vector<int> up(n, -1); {
stack<int> stk;
repeat (i,n) {
while (not stk.empty() and a[stk.top()] < a[i]) {
int j = stk.top(); stk.pop();
up[j] = i;
}
stk.push(i);
}
}
vector<int> down(n, -1); {
stack<int> stk;
repeat (i,n) {
while (not stk.empty() and a[stk.top()] > a[i]) {
int j = stk.top(); stk.pop();
down[j] = i;
}
stk.push(i);
}
}
// make table for doubling
int log_n = log2(n);
vector<vector<int> > skip(log_n, vector<int>(n, -1));
repeat (i,n) if (down[i] != -1) skip[0][i] = up[down[i]];
repeat (k,log_n-1) {
repeat (i,n) if (skip[k][i] != -1) {
skip[k+1][i] = skip[k][skip[k][i]];
}
}
// make table of last place
vector<int> end(n, -1);
repeat_reverse (i,n) {
end[i] = i;
repeat_reverse (k,log_n) {
if (skip[k][i] != -1) {
end[i] = end[skip[k][i]];
break;
}
}
}
repeat (i,n) {
if (down[end[i]] != -1) {
end[i] = down[end[i]];
}
}
// output
int q; cin >> q;
repeat (i,q) {
int x, y; cin >> x >> y; -- x; -- y;
bool ans = false;
if (end[x] == end[y]) {
ans = true;
} else {
// doubling
int z = x;
repeat_reverse (k,log_n) {
int nz = skip[k][z];
if (nz != -1 and nz <= y) z = nz;
}
int nz = down[z];
if (nz != -1 and nz <= y) z = nz;
if (z == y) ans = true;
}
cout << ans << endl;
}
return 0;
}