洛谷1600 天天爱跑步

原题链接

要解决这题有一个很重要的思想,就是将跑步的路径拆开来,分成向上走的(S o LCA(S,T)),及向下走的(LCA(S,T) o T)(S)是路径起点,(T)是路径终点)。
然后对于两种路径单独统计贡献。
先只考虑向上走的路径(S o LCA(S,T))
对于一个观测点(i)来说,只有满足(deep[S]-w[i]=deep[i])(deep)表示点在树中的深度)的路径才能对它产生贡献。移项得(deep[S]=deep[i]+w[i]),于是我们可以对树进行(dfs),并统计。
用一个桶(up)来统计当前贡献。
当搜到第(i)个点时(显然它的子树都已经统计完了),先在桶里加入从该点出发的路径,即(up[deep[i]]+=SUM\_S[i])
然后计算能对该点产生贡献的路径数,即(ans[i]+=up[deep[i]+w[i]])(注意(deep[i]+w[i])是可能越界的)
在退出该点时,从桶里减去向上走的路径中到达该点就不往上走的路径,即该点就是(LCA(S,T)),向上走路径的终点。
注意因为一棵树里有很多同一深度的点,我们统计的不一定是同一棵子树下的,所以我们可以在进入该点时记录下原始的(up[deep[i]+w[i]])值,在将子树搜索完后,用新的值减去原始值即是这棵子树所产生的贡献。
而对于向下走的路径(LCA(S,T) o T)的统计方法类似,只有满足(deep[T]-deep[i]=length-w[i])(length)表示路径长度)的路径才能对它产生贡献。移项得(deep[T]-length=deep[i]-w[i]),于是我们可以相似的用桶在(dfs)中统计。
当搜到第(i)个点时,一样先在桶里加入终点为该点的路径(因为(dfs)统计是向上统计,所以要加入终点),然后统计能对该点产生贡献的路径数。
在退出该点时,从桶里减去向下走路径中起点为该点的路径,即该点是(LCA(S,T))
注意(deep[i]-w[i])(deep[T]-length)可能是负数,所以需要加上一个数,平移数组。
最后将重复计算的(LCA)的贡献减(1)即可。
(LCA)部分可用倍增,或是(tarjan),这里我是用的倍增。

#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
const int N = 3e5 + 10;
const int M = N << 1;
const int K = 3e5;
struct ts {
	int x, y, L, LCA;
};
ts a[N];
int fi[N], di[M], ne[M], f[N][20], de[N], an[N], ss[N], up[N], dn[M], w[N], mdp, l, gn;
vector<int>ule[N], dmo[N], dle[N];
inline int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c < '0' || c > '9'; c = getchar())
		p |= c == '-';
	for (; c >= '0' && c <= '9'; c = getchar())
		x = x * 10 + c - '0';
	return p ? -x : x;
}
inline void add(int x, int y)
{
	di[++l] = y;
	ne[l] = fi[x];
	fi[x] = l;
}
inline int maxn(int x, int y)
{
	return x > y ? x : y;
}
inline void sw(int &x, int &y)
{
	int z = x;
	x = y;
	y = z;
}
void dfs(int x)
{
	int i, y;
	mdp = maxn(mdp, de[x]);
	for (i = 1; i <= gn; i++)
		f[x][i] = f[f[x][i - 1]][i - 1];
	for (i = fi[x]; i; i = ne[i])
		if ((y = di[i]) ^ f[x][0])
		{
			de[y] = de[x] + 1;
			f[y][0] = x;
			dfs(y);
		}
}
int lca(int x, int y)
{
	int i;
	if (de[x] > de[y])
		sw(x, y);
	for (i = gn; ~i; i--)
		if (de[f[y][i]] >= de[x])
			y = f[y][i];
	if (!(x ^ y))
		return x;
	for (i = gn; ~i; i--)
		if (f[x][i] ^ f[y][i])
		{
			x = f[x][i];
			y = f[y][i];
		}
	return f[x][0];
}
void dfs_up(int x)
{
	int i, y, la, nw = w[x] + de[x], si = ule[x].size();
	if (nw <= mdp)
		la = up[nw];
	for (i = fi[x]; i; i = ne[i])
		if ((y = di[i]) ^ f[x][0])
			dfs_up(y);
	up[de[x]] += ss[x];
	if (nw <= mdp)
		an[x] = up[nw] - la;
	for (i = 0; i < si; i++)
		up[ule[x][i]]--;
}
void dfs_down(int x)
{
	int i, y, la, nw = de[x] - w[x], si_1 = dmo[x].size(), si_2 = dle[x].size();
	la = dn[nw + K];
	for (i = fi[x]; i; i = ne[i])
		if ((y = di[i]) ^ f[x][0])
			dfs_down(y);
	for (i = 0; i < si_1; i++)
		dn[dmo[x][i] + K]++;
	an[x] += dn[nw + K] - la;
	for (i = 0; i < si_2; i++)
		dn[dle[x][i] + K]--;
}
int main()
{
	int i, n, m, x, y;
	n = re();
	m = re();
	gn = log2(n);
	for (i = 1; i < n; i++)
	{
		x = re();
		y = re();
		add(x, y);
		add(y, x);
	}
	de[1] = 1;
	dfs(1);
	for (i = 1; i <= n; i++)
		w[i] = re();
	for (i = 1; i <= m; i++)
	{
		a[i].x = re();
		a[i].y = re();
		ss[a[i].x]++;
		a[i].LCA = lca(a[i].x, a[i].y);
		a[i].L = de[a[i].x] + de[a[i].y] - (de[a[i].LCA] << 1);
		ule[a[i].LCA].push_back(de[a[i].x]);
		dmo[a[i].y].push_back(de[a[i].y] - a[i].L);
		dle[a[i].LCA].push_back(de[a[i].y] - a[i].L);
	}
	dfs_up(1);
	dfs_down(1);
	for (i = 1; i <= m; i++)
		if (!((de[a[i].x] - w[a[i].LCA]) ^ de[a[i].LCA]))
			an[a[i].LCA]--;
	for (i = 1; i <= n; i++)
		printf("%d ", an[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/Iowa-Battleship/p/9720563.html