20261110 模拟赛 T4 infect 做题记录

20261110 模拟赛 T4 infect 做题记录

原题:#12111. KazHackStan

思路

首先发现一个病毒的传播范围其实是一个树上圆,所以往树上圆理论上去想。由于题目给的是病毒的出现位置和时间,不太好直接转换成 \((o,\ r)\) 的表示方式,同时发现两个病毒的传播范围第一次相交时,实际上相当于一个新病毒产生,所以我们不妨直接将一个病毒表示为 \((o,\ t)\) ,两个病毒求交时可以先将时间小的病毒的圆心向另一个病毒移动,直至时间相同,之后用类似树上圆求交的方法处理即可。

接着就好处理了,用线段树维护一段病毒区间求交后的圆,支持单修区查即可,可以将每个查询转换为求 $ \sum_\limits{v=1}^n[dis_{u,v} \le d] $ 。这是点分治板子,离线下来做即可。(也可以用点分树在线做,每个点用 vector 动态开树状数组;或者也有魔改 DSU On Tree 的做法,比较复杂)

时间复杂度: \(\mathcal{O}(n\log^2 n)\) ,空间复杂度: \(\mathcal{O}(n)\)

Code
#include<bits/stdc++.h>
#define LL long long
#define PLI pair<LL, int>
#define PIL pair<int, LL>
#define PII pair<int, int>
#define PLL pair<LL, LL>
using namespace std;
const int N = 2e5, Q = 1e5, inf = 1e9;
int n, m, q, ct, cq, ans[Q + 5];
vector<int> g[N + 5];
int ncnt, son[N + 5], siz[N + 5], dep[N + 5], fa[N + 5];
int dfn[N + 5], rnk[N + 5], top[N + 5];
vector<PII> qy[N + 5];
void dfs0(int u = 1, int p = 0) {
	siz[u] = 1; son[u] = -1; fa[u] = p;
	for (auto v : g[u]) if (v != p) {
		dep[v] = dep[u] + 1;
		dfs0(v, u);
		siz[u] += siz[v];
		if (son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
	}
}
void dfs1(int u = 1, int tp = 0) {
	dfn[u] = ++ncnt; rnk[ncnt] = u; top[u] = tp;
	if (son[u] != -1) dfs1(son[u], tp);
	for (auto v : g[u]) if (v != fa[u] && v != son[u]) dfs1(v, v);
}
inline int lift(int u, int k) {
	while (dep[u] - dep[top[u]] < k) k -= (dep[u] - dep[top[u]] + 1), u = fa[top[u]];
	return rnk[dfn[u] - k];
}
inline int lca(int u, int v) {
	while (top[u] != top[v]) {
		if (dep[top[u]] < dep[top[v]]) swap(u, v);
		u = fa[top[u]];
	}
	return rnk[min(dfn[u], dfn[v])];
}
inline int mov(int u, int v, int k) {
	int kg = lca(u, v);
	int d = dep[u] + dep[v] - 2 * dep[kg];
	if (dep[u] - dep[kg] >= k) return lift(u, k);
	return lift(v, d - k);
}
inline int dis(int u, int v) { return dep[u] + dep[v] - 2 * dep[lca(u, v)]; }
struct Circle {
	int o; LL t;
	inline Circle operator * (const Circle& oth) const {
		Circle ret = {0, 0};
		Circle x = {o, t}, y = {oth.o, oth.t};
		if (x.t > y.t) swap(x, y);
		int u = x.o, v = y.o, d = dis(u, v);
		if (y.t - x.t >= d) return {v, y.t};
		u = mov(u, v, y.t - x.t);
		ret.o = mov(u, v, (d - (y.t - x.t)) >> 1);
		ret.t = y.t + ((d - (y.t - x.t)) >> 1);
		return ret;
	}
};
struct SGT {
	struct node {
		Circle c;
	}tree[N << 2];
#define lc (id << 1)
#define rc (lc + 1)
#define mid (l + r >> 1)
	inline void pushup(int id) { tree[id].c = tree[lc].c * tree[rc].c; }
	void build(int l = 1, int r = n, int id = 1) {
		tree[id].c = {1, -inf};
		if (l == r) return;
		build(l, mid, lc); build(mid + 1, r, rc);
	}
	void update(const int pos, const Circle v, int l = 1, int r = q, int id = 1) {
		if (l == r) return tree[id].c = v, void(0);
		if (pos <= mid) update(pos, v, l, mid, lc);
		else update(pos, v, mid + 1, r, rc);
		pushup(id);
	}
	Circle query(const int z, const int y, int l = 1, int r = q, int id = 1) {
		if (z <= l && r <= y) return tree[id].c;
		if (z <= mid && mid < y) return query(z, y, l, mid, lc) * query(z, y, mid + 1, r, rc);
		if (z <= mid) return query(z, y, l, mid, lc);
		return query(z, y, mid + 1, r, rc);
	}
#undef lc
#undef rc
#undef mid
}sgt;
struct BIT {
	int s[N + 5];
	inline int lowbit(const int i) { return i & (-i); }
	inline void update(int i, const int v) { i++; for (; i <= ct + 1; i += lowbit(i)) s[i] += v; }
	inline int query(int i) {
		i++;
		int ret = 0;
		for (; i > 0; i -= lowbit(i)) ret += s[i];
		return ret;
	}
}w;
int tsiz, rt, mimx;
bitset<N + 5> vis;
void get_siz(int u, int p = 0) {
	tsiz++;
	for (auto v : g[u]) if (v != p && !vis[v]) get_siz(v, u);
}
void get_rt(int u, int p = 0) {
	siz[u] = 1; int mx = 0;
	for (auto v : g[u]) if (v != p && !vis[v]) get_rt(v, u), mx = max(mx, siz[v]), siz[u] += siz[v];
	mx = max(mx, tsiz - siz[u]);
	if (mx < mimx) {
		mimx = mx;
		rt = u;
	}
}
void dfs2(int u, int p = 0) {
	dfn[u] = ++ncnt; rnk[ncnt] = u; siz[u] = 1;
	for (auto v : g[u]) if (v != p && !vis[v]) dep[v] = dep[u] + 1, dfs2(v, u), siz[u] += siz[v];
}
inline void calc(int u) {
	for (auto v : g[u]) if (!vis[v])
		for (int i = dfn[v]; i < dfn[v] + siz[v]; i++) if (rnk[i] <= n) w.update(dep[rnk[i]], 1);
	if (u <= n) w.update(0, 1);
	for (auto i : qy[u]) ans[i.second] += w.query(i.first); 
	for (auto v : g[u]) if (!vis[v]) {
		for (int i = dfn[v]; i < dfn[v] + siz[v]; i++) if (rnk[i] <= n) w.update(dep[rnk[i]], -1);
		for (int i = dfn[v]; i < dfn[v] + siz[v]; i++)
			for (auto j : qy[rnk[i]]) ans[j.second] += w.query(j.first - dep[rnk[i]]);
		for (int i = dfn[v]; i < dfn[v] + siz[v]; i++) if (rnk[i] <= n) w.update(dep[rnk[i]], 1);
	}
	if (u <= n) w.update(0, -1);
	for (auto v : g[u]) if (!vis[v])
		for (int i = dfn[v]; i < dfn[v] + siz[v]; i++) if (rnk[i] <= n) w.update(dep[rnk[i]], -1);
}
void build(int u) {
	mimx = inf; rt = 0; tsiz = 0;
	get_siz(u); get_rt(u);
	u = rt; vis[u] = 1; ncnt = 0; dep[u] = 0;
	dfs2(u);
	calc(u);
	for (auto v : g[u]) if (!vis[v]) build(v);
}
inline void init() {
	ct = n; cq = 0; ncnt = 0; m = 0; vis.reset(); sgt.build();
	for (int i = 0; i <= 2 * n + 1; i++) g[i].clear(), qy[i].clear();
	for (int i = 0; i <= q + 1; i++) ans[i] = 0;
}

inline void solve() {
	cin >> n >> q; init();
	for (int i = 1; i < n; i++) {
		int u, v; cin >> u >> v;
		g[u].push_back(++ct);
		g[ct].push_back(u);
		g[ct].push_back(v);
		g[v].push_back(ct);
	}
	dfs0(); dfs1();
	for (int i = 1; i <= q; i++) {
		int o; cin >> o;
		if (o == 1) {
			int v, t; cin >> v >> t;
			sgt.update(++m, (Circle) { v, t << 1 });
		} else {
			int l, r; LL t; cin >> l >> r >> t; t <<= 1;
			Circle ret = sgt.query(l, r);
			if (t < ret.t) ans[++cq] = 0;
			else qy[ret.o].push_back({min((LL)ct, t - ret.t), ++cq});
		}
	}
	build(1);
	for (int i = 1; i <= cq; i++) cout << ans[i] << "\n";
}
signed main() {
	freopen("infect.in", "r", stdin);
	freopen("infect.out", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int cc, tt = 1;
	cin >> cc >> tt;
	while (tt--) solve();
	return 0;
}
posted @ 2026-11-10 21:58  千千的咲  阅读(2)  评论(0)    收藏  举报