题解:P14588 [LNCPC 2025] 前线支援
LinkCatTree · · 题解
树剖常数小,BIT 小常数,两只老哥拿下最优解(截止 2026/11/6)。
令
思考怎么去维护操作 1。设
先仅考虑
再考虑
让我们头疼的是最后一项。转而考虑不同
看起来操作 1 已经变得简单了,考虑怎么做操作 2。对于子树
两部分贡献均可以用树状数组完成(注意要区间修改区间查询,所以实际上需要维护七棵 BIT),加上树剖是
%:include <bits/stdc++.h>
using namespace std;
# define il inline
using i64 = long long;
using u32 = unsigned int;
using u64 = unsigned long long;
namespace fast {
constexpr size_t L = 1u << 20;
char ibuf [L];
size_t ip = 0, ilen;
il char gc () {
if (__builtin_expect (ip >= ilen, 0)) {
ilen = fread (ibuf, sizeof (char), L, stdin);
ip = 0;
if (!ilen)
return 0;
}
return ibuf [ip++];
}
char obuf [L];
size_t op = 0;
il void pc (char c) {
if (__builtin_expect (op == L, 0)) {
fwrite (obuf, sizeof (char), op, stdout);
op = 0;
}
obuf [op++] = c;
}
struct flusher {
~flusher () {
if (op) {
fwrite (obuf, sizeof (char), op, stdout);
}
}
} __flusher__;
}
# define getchar fast :: gc
# define putchar fast :: pc
template <class _Tp>
il void read (_Tp &x) {
char ch = getchar ();
int negative = 0;
while (!isdigit (ch)) {
negative = ch == '-';
ch = getchar ();
}
x = 0;
while (isdigit (ch)) {
x = x * 10 + (ch & 15);
ch = getchar ();
}
negative ? (x = -x) : 0;
}
template <class _Tp>
il void write (_Tp x) {
if (x == 0) {
putchar ('0');
return ;
}
if (x < 0) {
putchar ('-');
x = -x;
}
static char stk [32];
int tp = 0;
while (x) {
stk [++tp] = x % 10 + 48;
x /= 10;
}
while (tp)
putchar (stk [tp--]);
}
constexpr int N = 5e5 + 5;
int n, q, fa [N], w [N];
vector <int> edg [N];
int siz [N], son [N], d [N];
void pre_dfs (int u, int fa) {
d [u] = d [fa] + w [u];
siz [u] = 1;
for (const int v : edg [u]) {
pre_dfs (v, u);
siz [u] += siz [v];
if (siz [v] > siz [son [u]])
son [u] = v;
}
}
constexpr int Lg = 20;
int top [N], dfn [N], out [N], rnk [N], tim;
int g [Lg][N];
void top_dfs (int u, int tp) {
top [u] = tp;
rnk [dfn [u] = ++tim] = u;
g [0][tim] = fa [u];
if (son [u])
top_dfs (son [u], tp);
for (const int v : edg [u])
if (v != son [u])
top_dfs (v, v);
out [u] = tim;
}
il int getm (int x, int y) {
return dfn [x] < dfn [y] ? x : y;
}
i64 pred [N], preh [N], pres [N];
void init () {
for (int i = 1; (1 << i) <= n; i++) {
const int gap = 1 << i;
const int half = 1 << i - 1;
for (int j = 1; j + gap - 1 <= n; j++)
g [i][j] = getm (g [i - 1][j], g [i - 1][j + half]);
}
for (int i = 1; i <= n; i++) {
const int u = rnk [i];
pred [i] = pred [i - 1] + d [u];
preh [i] = preh [i - 1] + w [u];
pres [i] = pres [i - 1] + w [u] * siz [u];
}
}
il int lca (int u, int v) {
if (u == v)
return u;
if ((u = dfn [u]) > (v = dfn [v]))
swap (u, v);
const int k = __lg (v - u++);
return getm (g [k][u], g [k][v - (1 << k) + 1]);
}
namespace bit1 {
i64 tc0 [N], tc1 [N];
i64 tb0 [N], tb1 [N];
il void add_c (int i, i64 v) {
const i64 vd = v * pred [i - 1];
for (int j = i; j <= n + 1; j += j & -j) {
tc0 [j] += v;
tc1 [j] += vd;
}
}
il void add_b (int i, i64 v) {
const i64 vj = v * i;
for (int j = i; j <= n + 1; j += j & -j) {
tb0 [j] += v;
tb1 [j] += vj;
}
}
il void add (int l, int r, i64 b) {
add_c (l, 1);
if (r < n)
add_c (r + 1, -1);
add_b (l, b);
if (r < n)
add_b (r + 1, -b);
}
il i64 pref_c (int x) {
i64 s0 = 0, s1 = 0;
for (int i = x; i; i -= i & -i) {
s0 += tc0 [i];
s1 += tc1 [i];
}
return pred [x] * s0 - s1;
}
il i64 pref_b (int x) {
i64 s0 = 0, s1 = 0;
for (int i = x; i; i -= i & -i) {
s0 += tb0 [i];
s1 += tb1 [i];
}
return (x + 1) * s0 - s1;
}
il i64 query (int l, int r) {
return pref_c (r) - pref_c (l - 1)
+ pref_b (r) - pref_b (l - 1);
}
}
namespace bit2 {
i64 bt0 [N], bth [N], bts [N];
il void add_b (int i, i64 v) {
const i64 vh = v * preh [i - 1];
const i64 vs = v * pres [i - 1];
for (int j = i; j <= n + 1; j += j & -j) {
bt0 [j] += v;
bth [j] += vh;
bts [j] += vs;
}
}
il void add (int l, int r) {
add_b (l, 1);
if (r < n)
add_b (r + 1, -1);
}
il i64 pref_t (int x) {
i64 s0 = 0, sh = 0;
for (int i = x; i; i -= i & -i) {
s0 += bt0 [i];
sh += bth [i];
}
return preh [x] * s0 - sh;
}
il i64 pref_s (int x) {
i64 s0 = 0, ss = 0;
for (int i = x; i; i -= i & -i) {
s0 += bt0 [i];
ss += bts [i];
}
return pres [x] * s0 - ss;
}
il i64 query_t (int l, int r) {
return pref_t (r) - pref_t (l - 1);
}
il i64 query_s (int l, int r) {
return pref_s (r) - pref_s (l - 1);
}
}
void operate (int x, int y) {
const int z = lca (x, y);
const i64 b = d [y] - d [z] * 2;
bit1 :: add (dfn [x], out [x], b);
if (x == z) {
while (top [y] != top [x]) {
bit2 :: add (dfn [top [y]], dfn [y]);
y = fa [top [y]];
}
if (x != y)
bit2 :: add (dfn [x] + 1, dfn [y]);
}
}
void query (int x) {
const i64 rs = bit1 :: query (dfn [x], out [x]);
const i64 rp = bit2 :: query_s (dfn [x], out [x]);
const int sz = siz [x];
i64 ans = rs - rp * 2;
if (x != 1) {
for (x = fa [x]; top [x] != 1; ) {
ans -= bit2 :: query_t (dfn [top [x]], dfn [x]) * sz * 2;
x = fa [top [x]];
}
if (x > 1)
ans -= bit2 :: query_t (dfn [1] + 1, dfn [x]) * sz * 2;
}
write (ans);
putchar ('\n');
}
int main ( /* QAQ */ ) {
# ifndef ONLINE_JUDGE
freopen ("ex_rescue_3.in", "r", stdin);
freopen ("tmp.out", "w", stdout);
# endif
read (n), read (q);
for (int i = 2; i <= n; i++) {
read (fa [i]), read (w [i]);
edg [fa [i]].emplace_back (i);
}
pre_dfs (1, 0);
top_dfs (1, 1);
init ();
for (int i = 1; i <= q; i++) {
int cmd;
read (cmd);
if (cmd == 1) {
int x, y;
read (x), read (y);
operate (x, y);
}
if (cmd == 2) {
int x;
read (x);
query (x);
}
}
fprintf (stderr, "%.3lf second (s)\n",
1. * clock () / CLOCKS_PER_SEC);
return 0;
}
提交记录。比 Solwek 的单老哥做法快近一倍。
感谢 Deepseek 教我如何把线段树改成树状数组。