来一发清新的80行 树剖 $LCA$ + 树上差分 题解。

-----from Judge

本题题意大概是给出一棵 n 个节点的树以及 m 条有向路径,

并且每个点 i 都有一个权值 $w[i]$,如果某条路径包含了 i 号节点,并且 i 号节点是该路径上的第 $w[i]$ 个节点的话就会对答案产生贡献。

考虑暴力做法

我们可以十分理所当然的想到一个暴力: u 和 v 向上跑,沿路判断条件并累加,直到跑到 $LCA$ 。

但是这个暴力的时间复杂度太高了: $O (n + m )$ 。于是我们考虑别的方法。

首先我们看 $n$ 和 $m$ 都是 $3e5$ 的数据范围,那么我们对于 $m$ 条路径的处理当然不能太大, 那么我们就考虑:用 **起点** 和 **终点** 来累加答案。

考虑路径的拆分

那么如何累加? 我们先考虑:每个节点要对答案产生贡献则必然出现在一条路径$(u->v)$ 上,又因为 $u$ 先会跑到 $LCA(u,v)$ ,然后再跑到 $v$。

即:$u$ -> $LCA(u,v)$ -> $v$

那么我们可以把路径拆成两条,分别处理 $u$ 到 $lca$ 路径上点的贡献,以及 $lca$ 到 $v$ 路径上点的贡献

考虑第一条路径上的点

我们可以推导出,在 $dep[i] + w[i] = dep[u]$ 的情况先, i 号节点会对答案产生贡献。

即:在 i 的子树内,若有 $dep[u] = dep[i] + w[i]$ (至于 i 不在 $u -> LCA$ 的路径上的情况我们可以用差分思想解决)

那么我们只需要判断 i 的子树内 有无 $dep[u]$ 满足该式即可,对此我们可以开一个计数数组 $cnt$ 。

然后对于一个点我们可以记录下当前点出发的节点数,每次深搜到该点就对计数数组累加,

同时我们扫描当前节点作为哪些子节点的 $LCA$ 出现过,将这些子节点的 $dep$ 减掉就可以达到差分的效果了。

考虑第二条路径上的点

类似的,我们可以推导出关于 u,v 和 i 的等式:
$dep[i] - w[i] = dep[v] - dis(u,v)$

那么这里 dep 减掉 w[i] 后可能是负数,对此我们将左右式同时加上 n 即可(题目条件:$w[i]<=n$)。

那么这样我们就要判断 i 的子树内是否有 $dep[v] - dis(u,v) = dep[i] - w[i]$ 即可。

然后同上操作。

遍历处理

那么我们只需要对树进行两次遍历就可以处理出以上信息了。

考虑重复贡献的删除

我们可以观察到,如果某个节点 $i$ 就是 $u$ 、$v$ 的 $LCA$
那么该节点的贡献是会被累加两次的。对此我们如何消除多余贡献?这里有两种方法:

暴力删除

在程序最后暴力枚举 m 条路径 的 LCA 并判断其是否在该路径上,在的话就减贡献。

修改遍历树的方式

其实我们完全可以在第二次遍历树的时候先对以 $i$ 为 $LCA$ 的路径上的终点信息先删除,然后再累加答案,相当于我们在做第二条路径的时候忽略掉 $LCA$ 这个节点。那么对此的操作也很简单,程序执行顺序换一下就好了。

 //by Judge
#include<iostream>
#include<vector>
#include<cstdio>
#define ll long long
using namespace std;
const int M=3e5+;
const int inf=1e9+;
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[<<],*p1=buf,*p2=buf;
inline int read(){
int x=,f=; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-''; return x*f;
} char sr[<<],z[];int C=-,Z;
inline void Ot(){fwrite(sr,,C+,stdout),C=-;}
inline void print(int x){
if(C><<)Ot();if(x<)sr[++C]=,x=-x;
while(z[++Z]=x%+,x/=);
while(sr[++C]=z[Z],--Z);sr[++C]=' ';
} int n,m,pat,mx;
int w[M],s[M],cnt1[M],cnt2[M<<],ans[M],siz[M],dep[M],son[M],f[M],top[M],head[M];
vector<int> q1[M],q2[M],q3[M];
struct operation{ int u,v,lca,dis; }a[M];
struct Edge{ int to,next; Edge(int to,int next):to(to),next(next){} Edge(){} }e[M<<];
inline void add(int u,int v){
e[++pat]=Edge(v,head[u]),head[u]=pat;
e[++pat]=Edge(u,head[v]),head[v]=pat;
}
#define v e[i].to
void dfs(int u,int fa){
siz[u]=,dep[u]=dep[f[u]=fa]+;
for(int i=head[u];i;i=e[i].next) if(v^fa){
dfs(v,u),siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
} void dfs(int u){
if(!top[u]) top[u]=u; if(!son[u]) return ;
top[son[u]]=top[u], dfs(son[u]);
for(int i=head[u];i;i=e[i].next)
if(v^f[u] && v^son[u]) dfs(v);
} void dfs1(int u){
int now=w[u]+dep[u],cun; if(now<=mx) cun=cnt1[now];
for(int i=head[u];i;i=e[i].next) if(v^f[u]) dfs1(v);
cnt1[dep[u]]+=s[u]; if(now<=mx) ans[u]=cnt1[now]-cun;
for(int i=;i<q1[u].size();++i) --cnt1[dep[q1[u][i]]];
} void dfs2(int u){
int now=dep[u]-w[u]+n,cum=cnt2[now];
for(int i=head[u];i;i=e[i].next) if(v^f[u]) dfs2(v);
for(int i=;i<q2[u].size();++i) ++cnt2[q2[u][i]+n];
for(int i=;i<q3[u].size();++i) --cnt2[q3[u][i]+n];
ans[u]+=cnt2[now]-cum;
}
#undef v
inline int LCA(int u,int v){
while(top[u]^top[v]){
dep[top[u]]>dep[top[v]]?u=f[top[u]]:v=f[top[v]];
} return dep[u]<dep[v]?u:v;
}
int main(){
n=read(),m=read();
for(int i=,u,v;i<n;++i)
u=read(),v=read(),add(u,v);
for(int i=;i<=n;++i) w[i]=read();
for(int i=;i<=m;++i) a[i].u=read(),a[i].v=read();
dep[]=,dfs(,),dfs(); for(int i=;i<=n;++i) mx=max(mx,dep[i]);
for(int i=;i<=m;++i){
a[i].lca=LCA(a[i].u,a[i].v),++s[a[i].u];
a[i].dis=dep[a[i].u]+dep[a[i].v]-dep[a[i].lca]*;
q1[a[i].lca].push_back(a[i].u);
} dfs1();
for(int i=;i<=m;++i){
q2[a[i].v].push_back(dep[a[i].v]-a[i].dis);
q3[a[i].lca].push_back(dep[a[i].v]-a[i].dis);
} dfs2();
for(int i=;i<=n;++i) print(ans[i]); return Ot(),putchar('\n'),;
}

Luogu:P1600 天天爱跑步的更多相关文章

  1. [NOIP 2016D2T2/Luogu P1600] 天天爱跑步 (LCA+差分)

    待填坑 Code //Luogu P1600 天天爱跑步 //Apr,4th,2018 //树上差分+LCA #include<iostream> #include<cstdio&g ...

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

    [luogu]P1600 [NOIP 2016]天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上 ...

  3. luogu P1600 天天爱跑步

    传送门 1A此题暴祭 (下面记点\(x\)深度为\(de_x\),某个时间点记为\(w_x\)) 首先,每条路径是可以拆成往上和往下两条路径的 对于往上的路径,假设有个人往上跑,\(w_y\)在点\( ...

  4. 洛谷P1600 天天爱跑步(线段树合并)

    小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nn ...

  5. P1600 天天爱跑步[桶+LCA+树上差分]

    题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵 ...

  6. 洛谷 P1600 天天爱跑步

    https://www.luogu.org/problemnew/show/P1600 (仅做记录) 自己的假方法: 每一次跑从a到b:设l=lca(a,b)对于以下产生贡献: a到l的链上所有的点( ...

  7. 洛谷P1600 天天爱跑步——树上差分

    题目:https://www.luogu.org/problemnew/show/P1600 看博客:https://blog.csdn.net/clove_unique/article/detail ...

  8. 洛谷P1600 天天爱跑步

    天天放毒... 首先介绍一个树上差分. 每次进入的时候记录贡献,跟出来的时候的差值就是子树贡献. 然后就可以做了. 发现考虑每个人的贡献有困难. 于是考虑每个观察员的答案. 把路径拆成两条,以lca分 ...

  9. P1600 天天爱跑步

    lca真心不太会,这里只介绍60分做法,100的太难辣简单了就不介绍了 n<=1000 zz回溯爆搜 S[i]全部相等 这dfs序都不用lca的,2333,差分,然后输出判断一下是否是0(1到i ...

随机推荐

  1. python 面向对象(一)初识面向对象

    ##################################总结#################### 1. 面向过程:一切以事物的发展流程为中心 面向对象:一切以对象为中心,一切皆为对向, ...

  2. eclipse+tomcat+maven+springmvc+mybatis+mysql集成WebService插件(Axis2+CXF)

    $1 环境介绍 $1.1 Eclipse Java EE IDE for Web Developers:Neon.2 Release (4.6.2) $1.2 Maven:3.3.9 $1.3 Spr ...

  3. 13个.Net开源的网络爬虫

    请点击此处输入图片描述 1:.Net开源的跨平台爬虫框架 DotnetSpider Star:430 DotnetSpider这是国人开源的一个跨平台.高性能.轻量级的爬虫软件,采用 C# 开发.目前 ...

  4. JAVA入门教程 - idea 新建maven spring MVC项目

    用的是Idea2017版本.其他大同小异 1.新建项目 2.勾选Create from archetype 选中maven-archetype-webapp 3.输入项目名字. 4.下一步 5.点Fi ...

  5. bash guide

    Table of Contents Basic Operations 1.1. File Operations 1.2. Text Operations 1.3. Directory Operatio ...

  6. 原生JavaScript运动功能系列(一):运动功能剖析与匀速运动实现

    在我们日常生活中运动就是必不可少的部分,走路.跑步.打篮球等.在网页交互设计上运动也是必不可少的部分,创建的网站交互设计运动模块有轮播图,下拉菜单,还有各种炫酷的游戏效果都跟运动密切相关.所以很重要, ...

  7. bzoj千题计划318:bzoj1396: 识别子串(后缀自动机 + 线段树)

    https://www.lydsy.com/JudgeOnline/problem.php?id=1396 后缀自动机的parent树上,如果不是叶子节点,那么至少有两个子节点 而一个状态所代表子串的 ...

  8. Spring boot 工具类静态属性注入及多环境配置

    由于需要访问MongoDB,但是本地开发环境不能直接连接MongoDB,需要通过SecureCRT使用127.0.0.2本地IP代理.但是程序部署到线上生产环境后,是可以直接访问MongoDB的,因此 ...

  9. MySQL数据库基本命令-1

    第一章:数据库概述1.数据(data) 数据库(DB) 数据库管理系统(DBMS) 数据库系统(DBS)2.数据库管理系统提供的功能: (1)数据定义语言:DDL (2)数据操作语言:DML 基本的数 ...

  10. docker搭建lnmp环境

    1.搭建lnmp网站平台 1.创建mysql数据库容器 docker run -it -d --name lnmp_mysql -p 3308:3306 -e MYSQL_ROOT_PASSWORD= ...