[NOIp2016]天天爱跑步 线段树合并
[NOIp2016]天天爱跑步
对于一个人,他的路程会分为两段,一段向上(根),一段向下,考虑在向上过程中他能产生贡献的观察者具有什么性质:设出发点深度为\(dep[x]\),观察者深度为\(dep[y]\),观察的时间为\(t\),需满足\(dep[x] - dep[y] = t\),换句话说就是\(dep[y] + t = dep[x]\)。向下那一段的推导类似,下面的部分也只以向上的路径为例来解释。
现在我们记录每个点下方出发点深度为\(dep[x]\)的人数,需要对于每个出发点更新出发点到出发点与目的地的lca的所有点,想到到开一颗权值线段树,用线段树合并不断把信息往上传,在lca处消除这次更新的影响,这样一来直接我们就可以在树上统计答案了。向下路径的处理与向上路径类似,这里就不再赘述,上代码。
#include <cstdio>
#include <cctype>
#include <vector>
#define R register
#define I inline
#define B 1000000
using namespace std;
const int N = 300001, M = 15000007;
char buf[B], *p1, *p2;
I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin), p1 == p2) ? EOF : *p1++; }
I int rd() {
R int f = 0;
R char c = gc();
while (c < 48 || c > 57)
c = gc();
while (c > 47 && c < 58)
f = f * 10 + (c ^ 48), c = gc();
return f;
}
int w[N], s[N], t[N], p[N], q[N], d[N], r[N], u[N], v[N], o[N], n, C, S;
struct T{int f, p, q;}e[M];
vector <int> g[N];
I void swp(int &x, int &y) { x ^= y, y ^= x, x ^= y; }
void dfs1(int x, int f) {
p[x] = f, d[x] = d[f] + 1, t[x] = 1;
for (R int i = 0, y, m = 0; i < s[x]; ++i)
if ((y = g[x][i]) ^ f) {
dfs1(y, x), t[x] += t[y];
if (t[y] > m)
m = t[y], q[x] = y;
}
}
void dfs2(int x, int t) {
r[x] = t;
if (q[x])
dfs2(q[x], t);
for (R int i = 0, y; i < s[x]; ++i)
if ((y = g[x][i]) ^ p[x] && y ^ q[x])
dfs2(y, y);
}
I int lca(int x, int y) {
while (r[x] ^ r[y]) {
if (d[r[x]] < d[r[y]])
swp(x, y);
x = p[r[x]];
}
if (d[x] > d[y])
swp(x, y);
return x;
}
void ins(int &k, int l, int r, int x, int v) {
if (!k)
k = ++C;
e[k].f += v;
if (l == r)
return ;
R int m = l + r >> 1;
if (x <= m)
ins(e[k].p, l, m, x, v);
else
ins(e[k].q, m + 1, r, x, v);
}
int mrg(int k, int t) {
if (!k)
return t;
if (!t)
return k;
e[k].f += e[t].f, e[k].p = mrg(e[k].p, e[t].p), e[k].q = mrg(e[k].q, e[t].q);
return k;
}
int qry(int k, int l, int r, int x) {
if (l == r)
return e[k].f;
R int m = l + r >> 1;
if (x <= m)
return qry(e[k].p, l, m, x);
else
return qry(e[k].q, m + 1, r, x);
}
void dfs(int x) {
for (R int i = 0, y; i < s[x]; ++i)
if ((y = g[x][i]) ^ p[x])
dfs(y), u[x] = mrg(u[x], u[y]), v[x] = mrg(v[x], v[y]);
o[x] = qry(u[x], 1, S, w[x] + d[x]) + qry(v[x], 1, S, w[x] - d[x] + n);
}
int main() {
R int m, i, x, y, a;
n = rd(), m = rd(), S = n << 1;
for (i = 1; i < n; ++i)
x = rd(), y = rd(), g[x].push_back(y), g[y].push_back(x);
for (i = 1; i <= n; ++i)
s[i] = g[i].size(), w[i] = rd();
dfs1(1, 0), dfs2(1, 1);
for (i = 1; i <= m; ++i)
x = rd(), y = rd(), a = lca(x, y), ins(u[x], 1, S, d[x], 1), ins(u[a], 1, S, d[x], -1), ins(v[y], 1, S, d[x] - (d[a] << 1) + n, 1), ins(v[p[a]], 1, S, d[x] - (d[a] << 1) + n, -1);
dfs(1);
for (i = 1; i <= n; ++i)
printf("%d ", o[i]);
return 0;
}
因为减的结果可能有负数,所以加上一个\(n\),值域变两倍。