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≤n1\leq s_i,t_i\leq n, 0\leq w_j\leq 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("\n");
    return 0;
}

原文地址:https://www.cnblogs.com/naruto-mzx/p/12150157.html

时间: 2024-10-08 12:25:27

luogu P1600 天天爱跑步 |树上差分+LCA的相关文章

Luogu P1600 天天爱跑步 树上差分

Luogu P1600 天天爱跑步 ### 树上差分 题目链接 树上问题 没看出怎么差分 被观察到的条件有两个 lca前一半(包括lca) \(d[S_i]-d[x]=w[x]\) \(d[i]\)表示节点深度 lca后一半 \(d[S_i]+d[x]-2*d[lca(S_i,T_i)]=w[x]\) 但是具体怎么实现这个公式?? 实现 \(d[S_i]=w[x]+d[x]\) 可以转化为线段树合并模型 在\(S_i\)到\(lca\)的路径上增加\(d[S_i]\)的价值 最后求出各点的\(w

[luogu]P1600 天天爱跑步[LCA]

[luogu]P1600 [NOIP 2016]天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含n个结点和n−1条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到n的连续正整数. 现在有m个玩家,第i个玩家的起点为Si?,终点为Ti? .每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速

bzoj4719: [Noip2016]天天爱跑步 树上差分

Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务.这个游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两 个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到N的连续正整数.现在有个玩家,第个玩家的 起点为Si ,终点为Ti .每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去,

[树上差分][lca] Luogu P3258 松鼠的新家

题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,......,最后到an,去参观新家.可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞.可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃. 维尼是个馋家伙,立马就答应了.现在松鼠希望知道为了保证维尼有

【COGS 2434】 暗之链锁 树上差分+LCA

差分就是把一个值拆成许多差的和如 1 2 4 6 9 那么 把这个东西拆成 1 1 2 2 3 就是了,当然也可以理解为对一个问题分解为多个子问题并对其进行操作来得到原问题的答案. 树上差分就更玄妙了,它既可以把原问题拆成他到根节点的所有点,也可以拆成子树,拆成子树的话修改一个点影响的是他到根的路径上所有点,根据这个我们可以再加上LCA来解决许多问题. 这道题:I. 我们可以看出我们可以把它转化成一棵有根树,那么两部分一定是一个子树和其他 II. 那些虚边,都是砍断实边之后的藕断丝连,至于如何计

poj3417 Network 树上差分+LCA

题目传送门 题目大意:给出一棵树,再给出m条非树边,先割掉一条树边,再割掉一条非树边,问有几种割法,使图变成两部分. 思路:每一条 非树边会和一部分的树边形成一个环,分三种情况: 对于那些没有形成环的树边来说,割掉这条边,就已经使图分离,然后随便割一条非树边就可以了,所以这样的边每次答案加上m. 对于那些只存在在一个环中的树边来说,割掉这条边,再割一条和他存在于同一个环中的那条非树边,也能合法,所以加一. 对于存在于多个环中的树边,无论怎样,都无法合法. 也就是此时我们将题目转化成了树上的覆盖问

P1600 天天爱跑步

lca真心不太会,这里只介绍60分做法,100的太难辣简单了就不介绍了 n<=1000 zz回溯爆搜 S[i]全部相等 这dfs序都不用lca的,2333,差分,然后输出判断一下是否是0(1到i的时间是固定的) 退化成一条链子 一个点i的ans就是i-time[i]和i+tim[i]的起点个数(当然要合法啦) 乱搞就好了,这里写的nlogn的 #include <iostream> #include <queue> #include <cstdio> #inclu

AcWing352 闇の連鎖(树上差分+lca)

这道题的我们知道如果在两个点之间有附加边,其实就相当于在这个回路上的每条边都权值+1,这样就可以通过差分数组来快速求取大小 这里的精髓就是在输入的两个位置+1,而在他们的lca上-=2: #include<iostream> #include<queue> #include<map> #include<vector> #include<cstdio> #include<algorithm> #include<stack>

BZOJ 4719--天天爱跑步(LCA&amp;差分)

4719: [Noip2016]天天爱跑步 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1464  Solved: 490[Submit][Status][Discuss] Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务.这个游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两 个结点,且任意两个结点存在一条路