题目链接
题目分析
虽然好像看起来不难,但是就是想不到啊
部分分感觉可以拿(60pts),但还没打代码验证完,分析暂时不放上来
2019/11/7 UPD:
把最后一档特殊点的部分分想了一下,这下应该(80pts)了,口胡一下部分分思路吧,不保证对
-
#1~#2:所有人起点等于自己终点:
起点所在点(w_i)若为(0)则能观测到,否则不能,其他点不能 -
#3~#4:(w_i = 0):
每个点上有起点则可被观测到,否则不能 -
#5:对于每条路径暴力模拟即可
-
#6~#8:树退化为一条链,,其中(1)与(2)有边,(2)与(3)有边,(cdots),(n - 1)与(n)有边
考虑每个观测点(i),它能观测到的点只可能是来自(i - w_i)和(i + w_i)两个点的,于是考虑在每个起点挂一个(vector)存储每条路径的长度,然后对于每个(vector)排一下序,对于每个查询直接定位到往前(w_i)个点和往后第(w_i)个点,在(vector)上二分找到第一个路径长度超过(w_i)的,然后更新答案,均摊复杂度(O(nlog2(n))) -
#9~#12:对于每条路径的终点在树上打一下(+1)标记,类似树上差分,然后查询(w_i = dep_i)的点的子树和即可
-
#13~#16:类似上一档部分分,查询每个观测点(i)满足(dep_i+w_i = dep_st)的子树中节点的起点个数
考虑在(dfs)序上做,记录一下每个节点的进栈序和出栈序,开桶维护(dep),扫描到每个进栈序的时候在桶中统计一下,对于一个节点的查询,记录它进栈和出栈时桶(dep_i + w_i)中的节点个数,相减得到增量即得到答案
(Luogu)题解第一页的@一扶苏一大佬写了每档部分分的分析,可能有些不一样,也可以去看他的
看一看(NOIP2016)年鉴,发现其实正解就是许多档部分分的想法的整合,这启示我们在拿到一道难题的时候,如果对正解暂时没有什么思路,可以考虑尽可能多的想部分分怎么打,一是你反正还是要对拍,二是部分分的做法可能内嵌(std)
从(forall s_i = 1)的部分分入手,发现(forall s_i = 1)时其实就是对于每一条路径头尾差分一下,然后对于每个(dep_i = w_i)的点统计一下它的子树和
那么正解呢
考虑把每一条路径拆成上行和下行两条路径,感觉和上面的部分分有共通之处,能不能继续考虑差分呢
答案是可以的
先考虑上行,还是在起点打(+1)标记,但是这样还不行,万一在(LCA)的地方拐下去了呢,所以还要在(LCA)的地方减一下
考虑这样的话一个点要满足什么条件
设起点为(st),那么有(dep_st - w_i = dep_i)时,从起点出发,跑过这个点的时候可以被观测到
移项一下 (dep_st = dep_i + w_i),即在(dep_st)这一层上打一下标记,查询时查询(dep_i + w_i)这层的子树和
下行类似,在终点(ed)处打标记,柿子列出来发现是(Len - (dep_ed - dep_i) = w_i),其中(Len)指跑步的路径长度
移项一下是(Len - dep_ed = w_i - dep_i),然后同上打一下标记,注意为了保证(LCA)处的贡献可以正常统计,上行和下行的(-1)标记有一个要打在(fa[LCA])上
对于每一层子树和的统计,我们对着每一个(dep)开一棵线段树,然后动态开点一下,查询子树和就是(dfs)序上连续一段
#include<bits/stdc++.h>
#define N (600000 + 10)
using namespace std;
inline int read() {
int cnt = 0, f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
return cnt * f;
}
int n, m, u, v, w[N], tot;
struct node {int s, t, l;} a[N];
int nxt[N], first[N], to[N];
void add(int x, int y) {nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
int fa[N], siz[N], dep[N], son[N], top[N], num[N], idx;
void dfs1(int cur, int father) {
fa[cur] = father, dep[cur] = dep[father] + 1, siz[cur] = 1;
for (register int i = first[cur]; i; i = nxt[i]) {
int v = to[i];
if (v == father) continue;
dfs1(v, cur);
siz[cur] += siz[v];
if (siz[son[cur]] < siz[v]) son[cur] = v;
}
}
void dfs2(int cur, int tp) {
top[cur] = tp, num[cur] = ++idx;
if (son[cur]) dfs2(son[cur], tp);
for (register int i = first[cur]; i; i = nxt[i]) {
int v = to[i];
if (num[v]) continue;
dfs2(v, v);
}
}
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 dep[u] < dep[v] ? u : v;
}
int ans[N];
int now, rt[N], ls[N * 20], rs[N * 20], val[N * 20];
void modify(int &x, int l, int r, int pos, int v) {
if (!x) x = ++now; val[x] += v; if (l == r) return;
int mid = (l + r) >> 1;
if (pos <= mid) modify(ls[x], l, mid, pos, v);
else modify(rs[x], mid + 1, r, pos, v);
}
int query(int x, int l, int r, int L, int R) {
if (!x) return 0; if (L <= l && R >= r) return val[x];
int mid = (l + r) >> 1, ans = 0;
if (L <= mid) ans += query(ls[x], l, mid, L, R);
if (R > mid) ans += query(rs[x], mid + 1, r, L, R);
return ans;
}
void clear() {for (register int i = 1; i <= now; ++i) ls[i] = rs[i] = val[i] = 0; memset(rt, 0, sizeof rt); now = 0;}
int main() {
n = read(), m = read();
for (register int i = 1; i < n; ++i) {
u = read(), v = read();
add(u, v), add(v, u);
}
dfs1(1, 0), dfs2(1, 1);
for (register int i = 1; i <= n; ++i) w[i] = read();
for (register int i = 1; i <= m; ++i) a[i].s = read(), a[i].t = read(), a[i].l = lca(a[i].s, a[i].t);
/*-----------go up------------*/
for (register int i = 1; i <= m; ++i) {
modify(rt[dep[a[i].s]], 1, n, num[a[i].s], 1);
if (fa[a[i].l]) modify(rt[dep[a[i].s]], 1, n, num[fa[a[i].l]], -1);
}
for (register int i = 1; i <= n; ++i) ans[i] = query(rt[dep[i] + w[i]], 1, n, num[i], num[i] + siz[i] - 1);
/*----------go up-------------*/
clear();
/*----------go down -----------*/
for (register int i = 1; i <= m; ++i) {
int Len = dep[a[i].t] + dep[a[i].s] - 2 * dep[a[i].l];
int cur = Len - dep[a[i].t] + n;
modify(rt[cur], 1, n, num[a[i].t], 1);
modify(rt[cur], 1, n, num[a[i].l], -1);
}
for (register int i = 1; i <= n; ++i) ans[i] += query(rt[w[i] - dep[i] + n], 1, n, num[i], num[i] + siz[i] - 1);
for (register int i = 1; i <= n; ++i) printf("%d ", ans[i]);
return 0;
}