MX galaxy Day19
maze
路径信息,发现树剖合并信息很慢,所以考虑点分治。
将每对询问挂在点分树的 \(LCA\) 上处理。
拼接路径就维护 \(f/g[u, l, r]\) 表示 \(rt\rightarrow u\) 的路径上值域为 \([l, r]\) 的 \(LDS/LIS\) 。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
#define ep(i, u, t) for (int i = H[u], t = e[i].v; i; i = e[i].n, t = e[i].v)
const int _ = 1e5 + 7;
typedef long long ll;
typedef std::bitset < _ > Bit;
struct edge { int v, n, w; }e[_ << 2]; int H[_], cnte = 1;
struct node { int x, y, i; };
int id, n, q, k; ll x, y, lx, ly, a, b, c, Ans, ans[_ << 5], f[_][12][12], g[_][12][12];
int pr[_], dep[_], sz[_], tmp[_], rt, Trt; Bit vis;
std::vector <int> C6H12O6[_];
std::vector <node> o[_];
void Add(int u, int v, int w) { e[++cnte] = { v, H[u], w }, H[u] = cnte; }
void getrt(int u, int f, int total) {
sz[u] = 1, tmp[u] = 0;
ep(i, u, v) if (!vis[v] and v != f)
getrt(v, u, total), sz[u] += sz[v], tmp[u] = std::max(tmp[u], sz[v]);
tmp[u] = std::max(tmp[u], total - sz[u]);
if (!rt or tmp[u] < tmp[rt]) rt = u;
}
int build(int u, int total, int d) {
rt = 0, getrt(u, 0, total); int t = rt; vis[t] = true, dep[t] = d;
ep(i, rt, v) if (!vis[v]) { int c = build(v, total - tmp[v], d + 1);
pr[c] = t, C6H12O6[t].push_back(c);
}
return t;
}
void Init() {
Trt = build(1, n, 1);
}
int Lca(int x, int y) {
while (dep[x] > dep[y]) x = pr[x];
while (dep[y] > dep[x]) y = pr[y];
while (x != y) x = pr[x], y = pr[y];
return x;
}
void dfs(int u, int fa) {
ep(i, u, v) if (!vis[v] and v != fa) { int w = e[i].w;
lep(j, w, k) lep(l, j, k)
f[v][w][l] = std::max(f[v][w][l], f[u][j][l] + 1);
lep(j, 1, w) lep(l, j, w)
g[v][j][w] = std::max(g[v][j][w], g[u][j][l] + 1);
lep(j, 1, k) lep(l, j, k)
f[v][j][l] = std::max(f[v][j][l], f[u][j][l]),
g[v][j][l] = std::max(g[v][j][l], g[u][j][l]);
dfs(v, u);
}
lep(l, 1, k) {
lep(j, 1, l) f[u][l][l] = std::max(f[u][l][l], f[u][j][l]);
if (l > 1) f[u][l][l] = std::max(f[u][l][l], f[u][l - 1][l - 1]);
}
rep(j, k, 1) {
lep(l, j, k) g[u][j][j] = std::max(g[u][j][j], g[u][j][l]);
if (j < k) g[u][j][j] = std::max(g[u][j][j], g[u][j + 1][j + 1]);
}
}
void cls(int u, int fa) {
lep(j, 1, k) lep(l, 1, k) f[u][j][l] = g[u][j][l] = 0;
ep(i, u, v) if (!vis[v] and v != fa) cls(v, u);
}
void calc(int u) {
if (o[u].size() == 0) return;
dfs(u, 0);
for (auto t : o[u]) {
lep(v, 1, k)
ans[t.i] = std::max(ans[t.i], f[t.x][v][v] + g[t.y][v][v]);
}
cls(u, 0);
}
void Solve(int u) {
calc(u), vis[u] = true;
for (int v : C6H12O6[u]) Solve(v);
}
int main() {
#ifndef DEBUG
freopen("maze.in", "r", stdin);
freopen("maze.out","w",stdout);
#endif
scanf("%d%d%d%d", & id, & n, & q, & k); int u, v, w;
lep(i, 2, n) scanf("%d%d%d", & u, & v, & w), Add(u, v, w), Add(v, u, w);
Init();
scanf("%lld%lld%lld%lld%lld", & lx, & ly, & a, & b, & c);
lep(Q, 1, q) {
x = (lx * a + ly * ly * b + c + 1ll * Q * Q) % n + 1,
y = (lx * lx * a + ly * b + c * c + Q) % n + 1;
o[Lca(x, y)].push_back({ x, y, Q }), lx = x, ly = y;
}
vis.reset(), Solve(Trt);
lep(Q, 1, q) Ans ^= 1ll * Q * ans[Q];
printf("%lld\n", Ans);
return 0;
}
kuroko
本题的难点在于利用空间。
我们只有两个数组,需要在 \(O(1)\) 的额外空间内解决问题。
所以我们需要尽可能的利用每个信息一遍,然后覆盖那份空间来存储别的信息。
我们肯定需要存储树的结构,考虑链式前向星,每个点有一个 \(Head\) 和 \(Nxt\) 。
我们只靠这两种信息来处理两种操作:
递归与回溯。
如果一个点有 \(Head\) 边,说明没有被遍历过,直接跳 \(Head\) 边并把 \(Head\) 边删掉。
否则已经无法递归,跳 \(Nxt\) 指针到其兄弟继续这个过程。
容易发现到这个时候其子树一定已经遍历完了,记录下来就得到了后缀遍历。
再反转就可以得到 \(dfn\) 序了。
点击查看
#include "dfs.h"
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
// vector defined in dfs.h
int tot, tmp, nw = 1;
void dfs(int n, Array &f, Array &a) {
lep(i, 1, n) a.set(i, i);
lep(i, 2, n) {
tmp = f[i];
f.set(i, a[tmp]), a.set(tmp, i);
}
while (nw) {
if (tmp = a[nw]) a.set(nw, 0), nw = tmp;
else tmp = f[nw], f.set(nw, ++tot), nw = tmp;
}
lep(i, 1, n) a.set(n - f[i] + 1, i);
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

点分树/DP+DP+ad-hoc
浙公网安备 33010602011771号