Luogu P1600 NOIP2016提高组 天天爱跑步 题解 [ 紫 ] [ 线段树合并 ] [ 树上差分 ]

news/2025/2/10 22:34:33/文章来源:https://www.cnblogs.com/zhr0102/p/18708898

天天爱跑步:这题主要难点还是想到根据 lca 分成两边,分讨算贡献。

思路

首先看到树上路径,很容易就能想到树上差分计算每个跑步的人的贡献。

那么一个人的贡献改如何计算呢?我们考虑某个观察员他在 \(w\) 秒到底能观察到谁,显然是能观察到路线经过他且到他的距离为 \(w\) 的人

因此,我们就可以根据路径上每个点到起点的距离来计算贡献。

而由于起点和终点的 lca 两边的点的距离计算方式不相同,所以需要进行分类讨论:

当路径未经过 lca 时

这时候观察员 \(x\) 能观察到人的条件是 \(dep_s-dep_x=w_x\),把有关观察员的变量全部丢到一边,得到 \(dep_s=dep_x+w_x\)

当路径经过 lca 时

这时候观察员 \(x\) 能观察到人的条件是 \(dep_s-dep_{lca}+dep_x-dep_{lca}=w_x\),把有关观察员的变量全部丢到一边,得到 \(dep_x-w_x=2\times dep_{lca}-dep_s\)

于是,我们就可以开桶记录下每个值对应的贡献,查询的时候答案就是 \(dep_x+w_x\) 的贡献与 \(dep_x-w_x\) 的和。同时注意特判 \(w_x=0\) 的情况,此时直接将两者的贡献相加会导致贡献多算一倍。

这个桶我们可以用动态开点线段树实现,然后做树上前缀和求答案的时候直接线段树合并即可。

注意这个线段树会存负数下标,且树上差分的时候要先在 lca 处减掉未经过 lca 的贡献,在 lca 的父亲处再减掉经过 lca 的贡献,不然会导致未经过 lca 的贡献被减两次或者经过 lca 的贡献被减两次。

时间复杂度 \(O(n\log n)\),因为树上差分一个操作要修改四次,所以空间记得开四倍。

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int N=300005;
int n,m,w[N],fa[N],top[N],dep[N],sz[N],son[N],ans[N];
vector<int>g[N];
struct Node{int ls,rs,v;
};
struct Segtree{Node tr[80*N];int root[N],tot=0;void update(int &u,int ln,int rn,int x,int k){if(u==0)u=++tot;tr[u].v+=k;if(ln==rn)return;int mid=(ln+rn)>>1;if(x<=mid)update(lc(u),ln,mid,x,k);else update(rc(u),mid+1,rn,x,k);}int merge(int x,int y){if(x==0||y==0)return x+y;tr[x].v+=tr[y].v;tr[x].ls=merge(lc(x),lc(y));tr[x].rs=merge(rc(x),rc(y));return x;}int query(int u,int ln,int rn,int x){if(ln==rn)return tr[u].v;int mid=(ln+rn)>>1;if(x<=mid)return query(lc(u),ln,mid,x);else return query(rc(u),mid+1,rn,x);}
}tr1;
void dfs1(int u,int f)
{dep[u]=dep[f]+1;fa[u]=f;sz[u]=1;for(auto v:g[u]){if(v==f)continue;dfs1(v,u);sz[u]+=sz[v];if(sz[son[u]]<sz[v])son[u]=v;}
}
void dfs2(int u,int tp)
{top[u]=tp;if(son[u]==0)return;dfs2(son[u],tp);for(auto v:g[u]){if(v==fa[u]||v==son[u])continue;dfs2(v,v);}
}
int getlca(int u,int v)
{while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]])swap(u,v);u=fa[top[u]];}if(dep[u]<dep[v])swap(u,v);return v;
}
void dfs3(int u,int f)
{for(auto v:g[u]){if(v==f)continue;dfs3(v,u);tr1.root[u]=tr1.merge(tr1.root[u],tr1.root[v]);}ans[u]=tr1.query(tr1.root[u],-N,N,dep[u]+w[u]);if(w[u])ans[u]+=tr1.query(tr1.root[u],-N,N,dep[u]-w[u]);
}
int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=1;i<n;i++){int u,v;cin>>u>>v;g[u].push_back(v);g[v].push_back(u);}dfs1(1,0);dfs2(1,1);for(int i=1;i<=n;i++)cin>>w[i];while(m--){int s,t;cin>>s>>t;int lca=getlca(s,t);int flca=fa[lca];tr1.update(tr1.root[s],-N,N,dep[s],1);tr1.update(tr1.root[t],-N,N,2*dep[lca]-dep[s],1);tr1.update(tr1.root[lca],-N,N,dep[s],-1);tr1.update(tr1.root[flca],-N,N,2*dep[lca]-dep[s],-1);}dfs3(1,0);for(int i=1;i<=n;i++)cout<<ans[i]<<" ";return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/881880.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Microsoft/Windows LAPS管理本地管理员密码

什么是LAPS Local Administrator Password Solution,简称LAPS,是微软提供的一种用于管理本地管理员密码的解决方案。用于在Active Directory(AD)环境中自动管理和轮换众多客户端Windows的本地管理员账户及密码。 在没有这个工具之前, 本地管理员密码管理会非常的复杂。比如…

Avalonia系列文章之布局简介

在UI设计中,页面布局非常重要,良好的布局不仅可以有效的利用空间,还能提升交互体验,以达到事半功倍的效果。所以对于Avalonia UI初学者来说,布局控件的了解与学习也非常的重要,今天以一些小例子,简述Avalonia UI框架中布局控件的使用,仅供学习分享使用,如有不足之处,…

ACM寒假集训第四期

ACM寒假集训第四期 有理数取余 思路 bx=a mod m x=((a mod m) * ( b^(-1) ) mod m)) mod m; 问题就转化为如何求解 b 的逆元 b x = 1 mod m ,b对m的逆元,当 gcd(b,m)=1 时才存在。存在 bx+my=1 通过辗转相除法可以得到b,m的最大公约数然后根据最后得到的一系列等式合并为 bx+m…

昆明理工大学2025年硕士研究生调剂汇总表(2月10日更新)

这是今年昆明理工大学调剂信息,目前只更新了部分学院的部分专业,后续会持续更新。 【腾讯文档】昆明理工大学2025年硕士研究生调剂汇总表 https://docs.qq.com/sheet/DZERIbnpPb3JjeHFO

推荐一款人人可用的开源 BI 工具,更符合国人使用习惯的数据可视化分析工具,数据大屏开发神器!

前言 今天大姚给大家推荐一款人人可用的开源、免费的 BI 工具,更符合国人使用习惯的数据可视化分析工具,数据大屏开发神器,Tableau、帆软的开源替代:DataEase。工具介绍 DataEase是一个开源的数据可视化分析工具,可以帮助用户快速分析数据并洞察业务趋势,从而实现业务的改…

清华大学推出的 DeepSeek 从入门到精通(104页)免费教程!

前言 最近 DeepSeek 的出现让 AI 在国内掀起了一股浪潮,各大媒体、平台都在讨论和推广 DeepSeek,帮助各行各样使用 AI 不再有困难。今天大姚给大家分享一个由清华大学推出的、免费的:DeepSeek从入门到精通实用教程,该教材有着足足的104页能够快速的帮助大家了解和使用DeepS…

卞俊良

卞俊良 你猜 你猜我猜不猜

P1314 [NOIP 2011 提高组] 聪明的质监员(前缀和)

这道题最核心就是二分W,然后对于每一个W都构建一个前缀和数组,但要注意ans不能开太小,我开INT_MAX都不行,其次就是二分左右端点的变化,如果s-y>0说明满足要求的点多了,这时候我们要提高标准,就是让W增大,及让beign=W+1,反之让end=W-1;如果s=y,此时ans=0;这时候我…

读读源码-ArrayList究竟是怎么实现增强for循环的?

以我的认知来说,java中的ArrayList实现List,List又是继承Collection,往后就不太清楚了。 今天看源码的时候发现Collection继承了Iterable,Iterable接口的作用是允许对象称为for-each loop语句的目标。 因此如果想了解ArrayList如何实现循环,我们必须先从Iterable看起 1.集…

“此电脑”不是“我的电脑”怎么办

在以前,我们可以按住win+E就可以打开到有盘符的界面(直接看见C盘,D盘,E盘……),现在win11没有这个效果了,我还得再在左边的选项里寻找“此电脑”再进入我想要的“我的电脑”界面,真麻烦!无缘无故多了几个点击步骤,效率真低! 今天,我想改变这个效果!自已自足!。。…