20261110 模拟赛 T4 infect 做题记录
20261110 模拟赛 T4 infect 做题记录
思路
首先发现一个病毒的传播范围其实是一个树上圆,所以往树上圆理论上去想。由于题目给的是病毒的出现位置和时间,不太好直接转换成 \((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;
}
浙公网安备 33010602011771号