luogu P1600 天天爱跑步 |树上差分+LCA

题目描述

小c 同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。

这个游戏的地图可以看作一一棵包含 nnn 个结点和 n−1n-1n−1 条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从 111 到 nnn 的连续正整数。

现在有 mmm 个玩家,第 iii 个玩家的起点为 sis_isi​,终点为 tit_iti​。每天打卡任务开始时,所有玩家在第 000 秒同时从自己的起点出发,以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去,跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树,所以每个人的路径是唯一的)

小c 想知道游戏的活跃度,所以在每个结点上都放置了一个观察员。在结点 jjj 的观察员会选择在第 wjw_jwj​ 秒观察玩家,一个玩家能被这个观察员观察到当且仅当该玩家在第 wjw_jwj​ 秒也理到达了结点 jjj 。小c 想知道每个观察员会观察到多少人?

注意:我们认为一个玩家到达自己的终点后该玩家就会结束游戏,他不能等待一 段时间后再被观察员观察到。 即对于把结点 jjj 作为终点的玩家:若他在第 wjw_jwj​ 秒前到达终点,则在结点 jjj 的观察员不能观察到该玩家;若他正好在第 wjw_jwj​ 秒到达终点,则在结点 jjj 的观察员可以观察到这个玩家。

输入格式

第一行有两个整数 nnn 和 mmm。其中 nnn 代表树的结点数量, 同时也是观察员的数量, mmm 代表玩家的数量。

接下来 n−1n-1n−1 行每行两个整数 uuu 和 vvv,表示结点 uuu 到结点 vvv 有一条边。

接下来一行 nnn 个整数,其中第 jjj 个整数为 wjw_jwj​ , 表示结点 jjj 出现观察员的时间。

接下来 mmm 行,每行两个整数 sis_isi​,和 tit_iti​,表示一个玩家的起点和终点。

对于所有的数据,保证 1≤si,ti≤n,0≤wj≤n1leq s_i,t_ileq n, 0leq w_jleq n1≤si​,ti​≤n,0≤wj​≤n。

输出格式

输出 111 行 nnn 个整数,第 jjj 个整数表示结点 jjj 的观察员可以观察到多少人。


【题解】洛谷1600天天爱跑步

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=6e5+10;
inline int read(){
	int x=0; char c=getchar();
	while(c<'0'||c>'9')c=getchar();
	while('0'<=c&&c<='9'){ x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
	return x;
}
int nxt[N<<1],head[N],go[N<<1],tot;
inline void add(int u,int v){nxt[++tot]=head[u],head[u]=tot,go[tot]=v,nxt[++tot]=head[v],head[v]=tot,go[tot]=u;}
int n,m,w[N],f[N][17];
struct node{
	int x,lca,op;
};
vector<node>a[N];
int dep[N];
void deal(int u,int fa){
	dep[u]=dep[fa]+1;
	for(int i=0;i<16;i++)f[u][i+1]=f[f[u][i]][i];
	for(int i=head[u];i;i=nxt[i]){
		int v=go[i];
		if(v==fa)continue;
		f[v][0]=u;
		deal(v,u);
	}
}
#define _ 10000
inline int LCA(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=16;i>=0;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i];
	if(x==y)return x;
	for(int i=16;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	return f[x][0];
}
int cnt1[N],cnt2[N],ans[N];
vector<int>del1[N],del2[N];
void dfs(int u,int fa){
	int x=cnt1[_+dep[u]+w[u]],y=cnt2[_+w[u]-dep[u]];
	for(int i=head[u];i;i=nxt[i]){
		int v=go[i];
		if(v==fa)continue;
		dfs(v,u);
	}
	for(int i=0;i<a[u].size();i++){
		int x=a[u][i].x,lca=a[u][i].lca;
		if(a[u][i].op){
			cnt1[_+dep[u]]++;
			del1[lca].push_back(dep[u]);
		}
		else {
			cnt2[_+dep[x]-2*dep[lca]]++;
			del2[lca].push_back(dep[x]-2*dep[lca]);
		}
	}
	for(int i=0;i<del1[u].size();i++)cnt1[_+del1[u][i]]--;
	ans[u]=cnt1[_+dep[u]+w[u]]-x+cnt2[_+w[u]-dep[u]]-y;
	for(int i=0;i<del2[u].size();i++)cnt2[_+del2[u][i]]--;
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<n;i++)add(read(),read());
	deal(1,1);
	for(int i=1;i<=n;i++)w[i]=read();
	for(int i=1,x,y,z;i<=m;i++){
		x=read(),y=read();
		z=LCA(x,y);
		a[x].push_back((node){y,z,1});
		a[y].push_back((node){x,z,0});
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)printf("%d ",ans[i]);
	printf("
");
	return 0;
}
原文地址:https://www.cnblogs.com/naruto-mzx/p/12150157.html