Determine Winning Islands in Race
线段树优化 dp 的好题啊!方便起见,题解中点的编号从 开始。
首先,很显然易见的是,只要在某个时刻 Elsie 的位置严格大于 Bessie 所在的位置,那么 Elsie 就可以获胜。因此,我们希望求出到所有点的最短距离。这是一张 DAG,这启发我们往动态规划的方向思考。
在初始时,Bessie 可以直接破坏起点让 Elsie 掉下去,Bessie 直接获胜。
再考虑当 Bessie 从第 个岛出发时,第 到 都是绝对不可能被 Bessie 破坏的,我们可以用 dp 直接求出从 到这些岛屿的最小距离。但是对于 之后的岛,我们不确定在到达时是否会被 Bessie 踩掉,所以我们只能确定到岛 的最小距离。
不过 Bessie 会破坏从 开始某个长度的连续区间,Elsie 若想超越 Bessie,必然是从 的某个岛屿通过备用桥直接跨过去。我们将岛 能一步到达的所有岛屿求出最小距离来。Bessie 到达 所用的时间为 。记 dp 数组为 ,所有无法到达的点计为 。若能找到一个 满足 ,即 Elsie 到达 所用时间小于 Bessie,就是超过了她,可以获胜。
对于每一个 都跑一遍 dp 即可求出答案。这样,我们有了一个 的做法,可喜可贺!再考虑如何优化。
下一轮,Bessie 会从第 个岛屿出发。由于 DAG,更新第 并不会影响 的值,因此 。如果我们在前一轮(第 轮)的时候还顺手考虑了连接 与 的主桥,那么根据 DAG,点 的答案其实也已经计算好了,即 。另外,安全的岛仅仅从 拓展到 ,仅多出了岛 ,那么只有与 直接相连的点才可能更新。综上,我们发现 与 相当相似,更新时可以直接压掉一维。
还有一件事:检查 Elsie 是否超过 Bessie 也是一轮 的!不过我们发现无论从哪个点开始 Bessie 到达每个点的最短路程都是斜率为 的一次函数,那么直接上经典技巧,给每个值都加上个偏移,就成求区间内是否有数小于定值了,用个取区间最小值的线段树维护就好了。
#include <bits/extc++.h>
using namespace std;
namespace pbds = __gnu_pbds;
istream &fin = cin;
ostream &fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using li = long long int;
class SGtree {
struct node {
size_t invl, invr;
uli minv;
node *nodel, *noder;
node(size_t l, size_t r) : invl(l), invr(r), minv() {
if (r - l == 1)
nodel = noder = nullptr;
else {
size_t mid = (r - l) / 2 + l;
nodel = new node(l, mid), noder = new node(mid, r);
}
}
bool in_interval(size_t l, size_t r) { return l <= invl && invr <= r; }
bool no_matter(size_t l, size_t r) { return invr <= l || r <= invl; }
uli getMin(size_t l, size_t r) {
if (no_matter(l, r))
return numeric_limits<uli>::max();
if (in_interval(l, r))
return minv;
return min(nodel->getMin(l, r), noder->getMin(l, r));
}
void set(size_t p, uli v) {
if (invl == p && invr - invl == 1)
return void(minv = v);
if (no_matter(p, p + 1))
return;
nodel->set(p, v), noder->set(p, v);
minv = min(nodel->minv, noder->minv);
}
} root;
public:
SGtree(size_t n) : root(0, n) {}
uli getMin(size_t l, size_t r) { return root.getMin(l, r); }
void set(size_t p, uli v) { return root.set(p, v); }
};
int main(void) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
size_t T;
fin >> T;
while (T--) {
size_t n, m;
cin >> n >> m;
vector<vector<size_t>> mp(n);
while (m--) {
size_t x, y;
cin >> x >> y;
if (y - x == 1)
continue;
mp[--x].emplace_back(--y);
}
SGtree f(n);
for (size_t i = 0; i < n; ++i)
f.set(i, numeric_limits<uli>::max() / 2);
f.set(0, n);
for (size_t p = 0; p < n - 1; ++p) {
cout << (f.getMin(p, n) < n - p ? '0' : '1');
ui x = f.getMin(p, p + 1) - (n - p);
for (size_t i : mp[p])
if (f.getMin(i, i + 1) - (n - i) > x + 1)
f.set(i, x + 1 + (n - i));
if (f.getMin(p + 1, p + 2) - (n - (p + 1)) > x + 1)
f.set(p + 1, x + 1 + (n - (p + 1)));
}
cout << '\n';
}
return 0;
}