NOIP2016 天天爱跑步
题目链接
题目分析
虽然好像看起来不难,但是就是想不到啊
部分分感觉可以拿\(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;
}