Loading

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);
}

posted @ 2025-08-01 20:16  qkhm  阅读(10)  评论(0)    收藏  举报