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

Description

小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。?天天爱跑步?是一个养成类游戏,需要

玩家每天按时上线,完成打卡任务。这个游戏的地图可以看作一一棵包含 N个结点和N-1 条边的树, 每条边连接两

个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从1到N的连续正整数。现在有个玩家,第个玩家的

起点为Si ,终点为Ti 。每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速度,

不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以

每个人的路径是唯一的)小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点的观察员会选

择在第Wj秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第Wj秒也理到达了结点J 。 小C想知道

每个观察员会观察到多少人?注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时

间后再被观察员观察到。 即对于把结点J作为终点的玩家: 若他在第Wj秒重到达终点,则在结点J的观察员不能观察

到该玩家;若他正好在第Wj秒到达终点,则在结点的观察员可以观察到这个玩家。

Input

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

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

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

接下来 M行,每行两个整数Si和Ti,表示一个玩家的起点和终点。

对于所有的数据,保证 。

1<=Si,Ti<=N,0<=Wj<=N

Output

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

首先可以得到两个式子

\(dep[u]-dep[x]=w[x]\)

\(dep[u]-2*dep[lca]+dep[x]=w[x]\)

换位得到

\(w[x]+dep[x]=dep[u]\)

\(w[x]-dep[x]=dep[u]-2*dep[lca]\)

\(x\)的值确定的时候,左边的值是确定的,因此我们考虑用x的子树更新\(x\)的答案。

用两个桶记录值,每\(dfs\)到一个点更新桶的值的个数。

用\(dep[u]\)和\(dep[u]-2*dep[lca]\)更新桶的情况,

用\(w[x]+dep[x]\)和\(w[x]-dep[x]\)查询\(dfs\)到\(x\)点时的答案。

注意\(w[x]-dep[x]\)的答案可能为负,所以要加上\(maxn\)

回溯时更新当前节点的答案,答案更新时减去父亲节点的答案

对于\(lca\)等于起点和终点的特殊考虑。

另外,对于分裂成两条链\(lca\)可能会被统计两遍,最后特殊判断一下,如果被统计了两遍就减去一遍,

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
const int maxn=3e5+10;
const int k=448;
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Dec(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
int n,m;
int fa[maxn],w[maxn],s[maxn],t[maxn];
vector<int>mp[maxn];
int son[maxn],size[maxn],top[maxn],dep[maxn];
struct que{int v,tag;};
vector<que>w1[maxn];
vector<que>w2[maxn];
int ans[maxn],lca[maxn];
void dfs1(int x,int f,int d){
    fa[x]=f;
    dep[x]=d;
    size[x]=1;
    for(int i=0;i<(int)mp[x].size();i++){
        int u=mp[x][i];
        if(u==f)continue;
        dfs1(u,x,d+1);
        size[x]+=size[u];
        if(!son[x]||size[son[x]]<size[u])son[x]=u;
    }
}
void dfs2(int x,int t){
    top[x]=t;
    if(!son[x])return ;
    dfs2(son[x],t);
    for(int i=0;i<(int)mp[x].size();i++){
        int u=mp[x][i];
        if(u==son[x]||u==fa[x])continue;
        dfs2(u,u);
    }
}
int LCA(int x,int y){
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]>dep[fy])x=fa[fx];
        else y=fa[fy];
        fx=top[x];fy=top[y];
    }
    if(dep[x]>dep[y])return y;
    else return x;
}
int bac1[2*maxn],bac2[2*maxn];
void dfs(int x,int a,int b){
    for(int i=0;i<(int)w1[x].size();i++)
//    For(i,0,(int)w1[x].size()-1) //不强制转换会RE
        bac1[w1[x][i].v+maxn]+=w1[x][i].tag;//更新桶内情况
    for(int i=0;i<(int)w2[x].size();i++)
//    For(i,0,(int)w2[x].size()-1)
        bac2[w2[x][i].v+maxn]+=w2[x][i].tag;//更新桶内情况
   for(int i=0;i<(int)mp[x].size();i++){
        int u=mp[x][i];
        if(u==fa[x])continue;
        dfs(u,bac1[dep[u]+w[u]+maxn],bac2[w[u]-dep[u]+maxn]);
    }
    ans[x]+=bac1[dep[x]+w[x]+maxn]+bac2[w[x]-dep[x]+maxn]-a-b;//a,b是父亲节点的答案,要减去父亲节点的情况就是当前节点的结果
}
int main()
{
    n=read();m=read();
    For(i,1,n-1){
        int u=read(),v=read();
        mp[u].push_back(v);
        mp[v].push_back(u);
    }
    For(i,1,n)w[i]=read();
    For(i,1,m)s[i]=read(),t[i]=read();
    dfs1(1,0,0);
    dfs2(1,1);
    For(i,1,m)lca[i]=LCA(s[i],t[i]);
    For(i,1,m){
        if(lca[i]==t[i]){
            w1[s[i]].push_back((que){dep[s[i]],1});
            w1[fa[t[i]]].push_back((que){dep[s[i]],-1});
        }
        else if(lca[i]==s[i]){
            w2[t[i]].push_back((que){dep[s[i]]-2*dep[lca[i]],1});
            w2[fa[s[i]]].push_back((que){dep[s[i]]-2*dep[lca[i]],-1});
        }
        else{
            if(w[lca[i]]+dep[lca[i]]==dep[s[i]])--ans[lca[i]];
            w1[s[i]].push_back((que){dep[s[i]],1});
            w1[fa[lca[i]]].push_back((que){dep[s[i]],-1});
            w2[t[i]].push_back((que){dep[s[i]]-2*dep[lca[i]],1});
            w2[fa[lca[i]]].push_back((que){dep[s[i]]-2*dep[lca[i]],-1});
        }
    }
    dfs(1,0,0);
    For(i,1,n)printf("%d ",max(0,ans[i]));
    return 0;
}

原文地址:https://www.cnblogs.com/Nan-Cheng/p/9735308.html

时间: 2024-08-07 11:22:38

bzoj4719: [Noip2016]天天爱跑步 树上差分的相关文章

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

bzoj4719[Noip2016]天天爱跑步

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

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

题目描述 小c 同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nnn 个结点和 n?1n-1n?1 条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从 111 到 nnn 的连续正整数. 现在有 mmm 个玩家,第 iii 个玩家的起点为 sis_isi?,终点为 tit_iti?.每天打卡任务开始时,所有玩家在第 000 秒同时

NOIP2016天天爱跑步

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

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 条边的树, 每条边连接两 个结点,且任意两个结点存在一条路

bzoj 4719: [Noip2016]天天爱跑步

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

LCA+线段树 NOIP2016 天天爱跑步

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

NOIP2016 天天爱跑步 正解

暴力移步 http://www.cnblogs.com/TheRoadToTheGold/p/6673430.html 首先解决本题应用的知识点: dfs序——将求子树的信息(树形)转化为求一段连续区间信息(线形) 线段树——求区间信息 树上差分——统计答案 lca——拆分路径 树链剖分——求lca 另deep[]表示节点的深度,watch[]表示观察者的出现时间,s表示玩家起点,t表示终点 固定节点的观察者出现的时间固定,说明对这个观察者有贡献的点是有限且固定的 只有满足  观察者出现时间=玩

noip2016 天天爱跑步

分析:这道题真心烦啊,是我做过noip真题中难度最高的一道了,到今天为止才把noip2016的坑给填满.暴力的话前60分应该是可以拿满的,后40分还是很有难度的. 定义:每个人的起点.终点:s,t;深度:deep[i];观察员出现时间:w[i]; 首先,树上两个点的最短路径肯定要经过LCA,那么对于路径x ---> y我们可以分成两部分:1.x ---> lca. 2.lca ---> y.先分析第一段路上的观察员i,显然s到i的距离等于w[i]才行,这段路是从x向上跳的,所以可以得到式