luogu P1600 天天爱跑步 |树上差分+LCA
Posted naruto-mzx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}
以上是关于luogu P1600 天天爱跑步 |树上差分+LCA的主要内容,如果未能解决你的问题,请参考以下文章