题解 [NOIP2016 提高组] 天天爱跑步
本以为自己对树上这些小操作玩得挺溜,但做了这道题还是发现有一些不到位的地方。
这么著名的题目就不用再写一遍了。
首先肯定是要研究一下能被观测到的条件,很套路地分成上下两部分讨论。
不难发现,一个点 \(u\) 能观测到的人必须满足 \(dep_u-w_u = dep_t-dis(s, t)\) 或 \(dep_u + w_u = dep_s\)。
然后这玩意儿怎么处理我就有点儿懵了,因为脑中想到树上差分就只是思维定式的想到转化成子树的和,而这个的和显然不能给它都加一遍,那就真的时间爆炸了。
由于这题的时空限制,不能给每个点都开一个桶并且转移数据,而是要全局共用一个桶。可以发现,原来的树上差分求的整个子树和是因为在子树里的链会把加一减一都算上去而抵消,留下的只是跨过这个点的。
那么在这里也只需要让其抵消即行了,在第一次到达一个点和第二次到达一个点之间,全局的桶随着 dfs 跑遍了下面的子树,虽然会进入不在这条链上的点,但到最终不跨过上面一个点的链都会被消掉,所以只要计算两次到达一个点的差就可以了。
这题的欧拉序 LCA 总算没有让我调上半天了。
代码
#include <iostream>
#include <vector>
#include <cmath>
#define puba push_back
const int N = 600006, S = 300001;
int n, m, a[N], tot, dep[N], in[N], f[N][20], b1[N], b2[N],
s[N], t[N], cnt[N], dis[N], ans[N], w[N], u, v;
std::vector<int> g[N], op1[N], op2[N];
void pre(int u, int fa) {
dep[u] = dep[fa] + 1;
in[u] = ++tot, a[tot] = u;
for (int i = 0; i < (int)g[u].size(); i++) {
int v = g[u][i];
if (v == fa) continue;
pre(v, u);
a[++tot] = u;
}
};
void init() {
for (int i = 1; i <= tot; i++) f[i][0] = a[i];
for (int j = 1; j <= 19; j++)
for (int i = 1; i <= tot; i++)
if (i+(1<<(j-1)) <= tot &&
dep[f[i][j-1]] > dep[f[i+(1<<(j-1))][j-1]])
f[i][j] = f[i+(1<<(j-1))][j-1];
else f[i][j] = f[i][j-1];
}
int lca(int x, int y) {
int l = in[x], r = in[y];
if (l > r) std::swap(l, r);
int k = log2(r-l+1);
if (dep[f[l][k]] < dep[f[r-(1<<k)+1][k]]) return f[l][k];
else return f[r-(1<<k)+1][k];
}
void dfs(int u, int fa) {
int t1 = b1[w[u]+dep[u]], t2 = b2[w[u]-dep[u]+S];
for (int i = 0; i < (int)g[u].size(); i++) {
int v = g[u][i];
if (v == fa) continue;
dfs(v, u);
}
b1[dep[u]] += cnt[u];
for (int i = 0; i < (int)op1[u].size(); i++)
b2[dis[op1[u][i]] - dep[t[op1[u][i]]] + S] ++;
ans[u] += b1[w[u]+dep[u]] - t1 + b2[w[u]-dep[u]+S] - t2;
for (int i = 0; i < (int)op2[u].size(); i++) {
int x = op2[u][i];
b1[dep[s[x]]] --, b2[dis[x]-dep[t[x]]+S] --;
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
std::cin >> n >> m;
for (int i = 1; i < n; i++) {
std::cin >> u >> v;
g[u].puba(v), g[v].puba(u);
}
pre(1, 0), init();
for (int i = 1; i <= n; i++) std::cin >> w[i];
for (int i = 1; i <= m; i++) {
std::cin >> s[i] >> t[i];
int l = lca(s[i], t[i]);
dis[i] = dep[s[i]] + dep[t[i]] - 2*dep[l];
cnt[s[i]] ++;
op1[t[i]].puba(i), op2[l].puba(i);
if (dep[l] + w[l] == dep[s[i]]) ans[l] --;
}
dfs(1, 0);
for (int i = 1; i <= n; i++) std::cout << ans[i] << ' ';
}

浙公网安备 33010602011771号