P1600 [NOIP 2016 提高组] 天天爱跑步

P1600 [NOIP 2016 提高组] 天天爱跑步

题目背景

NOIP2016 提高组 D1T2

题目描述

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

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

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

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

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

Solution:

偶然发现我居然没有写这题的结题报告?赶紧水一发。

首先对于这样的树上路径问题我们都可以把这条路径 (x,y) 拆成上行 (x,lca) 和下行 (lca,y) 两部分:假设一个人睁眼的时间是 wi 那么对于上行链,应该满足:depxdepi=wi,然后对于下行链:dis(x,y)(depydepi)=wi 然后我们移项:

{wi+depi=depxwidepi=dis(x,y)depy

我们维护两个桶然后树上差分就好了。

但是还要注意的是,由于我们的桶是全局的,所有我们应该消除子树外的节点的答案对于当前节点的影响。我们在统计答案时应该先算一下统计到这个点时桶的状态 tmp 然后这个点真正的答案是将要离开这个点时的答案 anstmp 作差。

然后这题就做完了。

Code:

#include<bits/stdc++.h>
using namespace std;
int n,m,cnt,cnta,cnte;
const int N=3e5+5;
const int lg=20;
int head[N],deep[N],fa[N][lg+5],heada[N],heade[N],s[N],e[N],num[N],w[N],dis[N];
struct Edge{
int to,ne;
}edge[N<<1];
struct Point{
int id,ne;
}lcap[N<<1],endp[N<<1];
void add(int x,int y)
{
edge[++cnt].to=y;edge[cnt].ne=head[x];head[x]=cnt;
}
void dfs1(int u,int f)
{
deep[u]=deep[f]+1;
fa[u][0]=f;
for(int i=1;i<=lg;i++)
{
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(int i=head[u];~i;i=edge[i].ne)
{
int v=edge[i].to;
if(v==f)continue;
dfs1(v,u);
}
return ;
}
int lca(int x,int y)
{
if(x==y)return x;
if(deep[x]<deep[y])swap(x,y);
for(int i=lg;i>=0;i--)
{
if(deep[fa[x][i]]>=deep[y])x=fa[x][i];
}
if(x==y)return x;
for(int i=lg;i>=0;i--)
{
if(fa[x][i]!=fa[y][i]){ x=fa[x][i];y=fa[y][i];}
}
return fa[x][0];
}
void adda(int id,int x)
{
lcap[++cnta].id=id;lcap[cnta].ne=heada[x];heada[x]=cnta;
}
void adde(int id,int x)
{
endp[++cnte].id=id;endp[cnte].ne=heade[x];heade[x]=cnte;
}
void init()
{
for(int i=1;i<=n;i++)
{
head[i]=heada[i]=heade[i]=-1;
}
}
int downline[N<<1],upline[N<<1],ans[N];
void dfs2(int u,int f)
{
int dowm=downline[w[u]-deep[u]+N],up=upline[w[u]+deep[u]];//
for(int i=head[u];~i;i=edge[i].ne)
{
int v=edge[i].to;
if(v==f)continue;
dfs2(v,u);
}
upline[deep[u]]+=num[u];//deep[lca]+w=deep[s];
for(int i=heade[u];~i;i=endp[i].ne)
{
int id=endp[i].id;
downline[dis[id]-deep[e[id]]+N]++;//dis=deep[e]-deep[lca]+w;
}
ans[u]+=downline[w[u]-deep[u]+N]-dowm+upline[w[u]+deep[u]]-up;
for(int i=heada[u];~i;i=lcap[i].ne)
{
int id=lcap[i].id;
downline[dis[id]-deep[e[id]]+N]--;
upline[deep[s[id]]]--;
}
return ;
}
int main()
{
// freopen("p1600.in","r",stdin);
// freopen("p1600.out","w",stdout);
cin>>n>>m;
init();
for(int i=1,x,y;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs1(1,1);
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&s[i],&e[i]);
int a=lca(s[i],e[i]);
dis[i]=deep[s[i]]+deep[e[i]]-deep[a]*2;
num[s[i]]++;
adda(i,a);
adde(i,e[i]);
if(deep[a]+w[a]==deep[s[i]])ans[a]--;
}
dfs2(1,1);
for(int i=1;i<=n;i++)
{
printf("%d ",ans[i]);
}
return 0;
}
posted @   liuboom  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示