P1600 [NOIP2016 提高组] 天天爱跑步

我只会这题的 (O(n log n)) 线段树合并做法……感觉树上差分的做法好妙。

首先把每条路径 (s_i o t_i) 拆成一条向上的路径和一条向下的路径:(s_i o operatorname{LCA}(s_i,t_i) o t_i)。记 (l_i=operatorname{LCA}(s_i,t_i))

对于向上的路径,可以发现,如果一条路径 (s_i o l_i) 对观察员 (u) 产生了贡献,那么 (uin anc_{s_i})(dep_{s_i}=dep_u+w_u)

对于向下的路径,推出来 (dep_{s_i}-2 imes dep_{l_i}=w_u-dep_u)

向上路径的做法和向下路径的做法类似,下面只讨论向上的做法。

考虑树上差分:将每种数看成一种物品,我们在点 (s_i) 打一个 (+dep_{s_i}) 的标记,在 (fa_{l_i}) 处打一个 (-dep_{s_i}) 的标记。

我们记录一个全局的数组 (c)(c_i) 表示物品 (i) 的个数。

我们对树进行 dfs。当 dfs 到点 (u) 时,先记录此时 (c_{w_u-dep_u}) 的值 (cnt),再 dfs 点 (u) 的所有儿子,然后将点 (u) 的所有标记作用到 (c) 数组上,此时点 (u) 作为观察员的答案就会加上 (c_{w_u-dep_u}-cnt)

为什么这是对的?因为 (c) 数组具有可减性,所以我们可以很方便地去除点 (u) 的祖先的贡献(通过减去 (cnt))。而且我们在更新点 (u) 的答案时,只遍历了点 (u) 的所有子孙。

代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &x){
	x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	x=x*_f;
}
template<typename T,typename... Args> void Read(T &x,Args& ...others){
	Read(x);Read(others...);
}
typedef long long ll;
const int N=3e5+5;
int n,m,w[N],s[N],lca[N],t[N];
vector<int> G[N];
int dep[N],siz[N],hson[N],fa[N];
void HLD1(int u){
	dep[u]=dep[fa[u]]+1,siz[u]=1;
	for(int v:G[u]){
		if(v==fa[u]) continue;
		fa[v]=u;
		HLD1(v);siz[u]+=siz[v];
		if(!hson[u]||siz[v]>siz[hson[u]]) hson[u]=v;
	}
}
int top[N];
void HLD2(int u,int tp){
	top[u]=tp;
	if(hson[u]) HLD2(hson[u],top[u]);
	else return;
	for(int v:G[u]){
		if(v==fa[u]||v==hson[u]) continue;
		HLD2(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]?v:u;
}
int ans[N],c[N<<1];vector<int> chg1[N];
void Dfs1(int u){
	int cnt=c[w[u]+dep[u]];
	for(int v:G[u]){
		if(v==fa[u]) continue;
		Dfs1(v);
	}
	for(int chg:chg1[u]){
		if(chg>0) ++c[chg];
		else --c[-chg];
	}
	ans[u]+=c[w[u]+dep[u]]-cnt;
}
vector<int> chg2[N];
void Dfs2(int u){
	int cnt=c[w[u]-dep[u]+n];
	for(int v:G[u]){
		if(v==fa[u]) continue;
		Dfs2(v);
	}
	for(int chg:chg2[u]){
		if(chg>0) ++c[chg];
		else --c[-chg];
	}
	ans[u]+=c[w[u]-dep[u]+n]-cnt;
}
int main(){
	Read(n,m);
	int u,v;
	For(i,1,n-1){
		Read(u,v);
		G[u].push_back(v),G[v].push_back(u);
	}
	HLD1(1);HLD2(1,1);
	For(i,1,n) Read(w[i]);
	For(i,1,m){
		Read(s[i],t[i]);lca[i]=Lca(s[i],t[i]);
		chg1[s[i]].push_back(dep[s[i]]);
		if(fa[lca[i]]) chg1[fa[lca[i]]].push_back(-dep[s[i]]);
		chg2[t[i]].push_back(dep[s[i]]-2*dep[lca[i]]+n);
		chg2[lca[i]].push_back(-dep[s[i]]+2*dep[lca[i]]-n);
	}
	Dfs1(1);
	memset(c,0,sizeof c);
	Dfs2(1);
	For(i,1,n) printf("%d ",ans[i]);
	return 0;
}
Written by Alan_Zhao
原文地址:https://www.cnblogs.com/alan-zhao-2007/p/p1600-sol.html