NOIP2016天天爱跑步
NOIP2016 天天爱跑步
题意:有一棵\(n\)个点的树,有\(m\)个玩家,每个玩家有一个起点\(S_i,T_i\),所有的玩家在第\(0\)秒从自己的起点出发,以每秒跑一条边的速度,沿着最短路径向终点跑。节点\(j\)的观察员会在第\(W_j\)秒观察此时该节点上的玩家,求每个观察员会观察到多少人?
数据范围:\(1<=n,m<=3e5\)
解法:
\(LCA\) + 桶 + 差分
先预处理出每个节点的深度\(dep_i\),以及每条路径的长度\(dis_i\)(用倍增\(LCA\))。
这里我们如果考虑观察员,会发现时间复杂度很难优化。所以我们考虑每个玩家对答案的贡献。
我们先把每条路径拆成向上的一段和向下的一段(最后的时候要注意起点和终点是否会对\(LCA\)重复贡献)
向上:考虑位于节点\(j\)的观察员,当且仅当\(S_i\)位于\(j\)的子树中且\(S_i,T_i\)的LCA不在\(j\)之上时,如果\(dep_{s_i} = dep_j+W_j\),则\(S_i\)会对\(j\)产生贡献。
向下同理,式子变为\(dis_i-(dep_{T_i}-dep_j)=W_j\)时会对\(j\)产生贡献。
我们用桶记录,每次出一个节点时用桶统计答案,并减掉这个点最为\(LCA\)时对这个点以上的点的答案的影响。
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 300100
int n, m;
int w[maxn], dep[maxn], dis[maxn];
int f1[maxn], f2[maxn * 2];
int st[maxn], ed[maxn], cnt[maxn], ans[maxn];
int f[maxn][22];
int fir[maxn], nxt[maxn * 2], vv[maxn * 2];
int fir1[maxn], nxt1[maxn * 2], vv1[maxn * 2];
int fir2[maxn], nxt2[maxn * 2], vv2[maxn * 2];
int tot, tot1, tot2;
void add(int u, int v)
{
nxt[++tot] = fir[u]; fir[u] = tot; vv[tot] = v; return;
}
void add1(int u, int v)
{
nxt1[++tot1] = fir1[u]; fir1[u] = tot1; vv1[tot1] = v; return;
}
void add2(int u, int v)
{
nxt2[++tot2] = fir2[u]; fir2[u] = tot2; vv2[tot2] = v; return;
}
void Deal_first(int u, int fa)
{
dep[u] = dep[fa] + 1;
for(int i = 0; i <= 19; i++)
f[u][i + 1] = f[f[u][i]][i];
for(int i = fir[u]; i; i = nxt[i])
{
int v = vv[i];
if(v == fa) continue;
f[v][0] = u;
Deal_first(v, u);
}
return;
}
int LCA(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
for(int i = 20; i >= 0; i--)
{
if(dep[f[x][i]] >= dep[y])
{
x = f[x][i];
}
if(x == y)
{
return x;
}
}
for(int i = 20; i >= 0; i--)
{
if(f[x][i] != f[y][i])
{
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
void dfs(int u, int fa)
{
// printf("u = %d fa = %d\n", u, fa);
int tmp1 = f1[w[u] + dep[u]], tmp2 = f2[w[u] - dep[u] + maxn];
for(int i = fir[u]; i; i = nxt[i])
{
int v = vv[i];
if(v == fa) continue;
dfs(v, u);
}
f1[dep[u]] += cnt[u];//以u为起点
for(int i = fir1[u]; i; i = nxt1[i])// 枚举以u为终点的情况 因为每个终点的dis值不同,故须记录一个链式前向星
{
int v = vv1[i];
// f2[dis[v] - dep[u] + maxn] += 1;
f2[dis[v] - dep[ed[v]] + maxn] += 1;
}
ans[u] += (f1[w[u] + dep[u]] - tmp1) + (f2[w[u] - dep[u] + maxn] - tmp2);
for(int i = fir2[u]; i; i = nxt2[i])
{
int v = vv2[i];
f1[dep[st[v]]] -= 1;
f2[dis[v] - dep[ed[v]] + maxn] -= 1;
}
return;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i < n; i++)
{
int u, v; scanf("%d%d", &u, &v);
add(u, v); add(v, u);
}
for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
Deal_first(1, 1); dep[1] = 1;// printf("makiah");
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &st[i], &ed[i]);
int fa = LCA(st[i], ed[i]);
// printf("fa = %d\n", fa);
dis[i] = dep[st[i]] + dep[ed[i]] - dep[fa] * 2;
cnt[st[i]]++;
add1(ed[i], i);
add2(fa, i);
if(dep[fa] + w[fa] == dep[st[i]]) ans[fa]--;
}
dfs(1, 0);
for(int i = 1; i <= n; i++) printf("%d ", ans[i]);
return 0;
}