题面

传送门

分析

next_id = 1
id = array of length n filled with -1
visited = array of length n filled with false function dfs(v):
visited[v] = true
id[v] = next_id
next_id += 1
for to in neighbors of v in increasing order:
if not visited[to]:
dfs(to)

观察题目中的这段伪代码,发现实际上就是求出每个节点的DFS序,

注意for to in neighbors of v in increasing order:,要按编号从小到大访问每个节点,所以要对邻接表排序(可以用vector实现)

对询问离线,每个结点保存由该节点出发所有询问

第一次DFS,

求出每个点到根节点的距离,以及DFS序。顺便把每个节点的子树对应的DFS序范围求出,记为l[x],r[x]

用一棵线段树存储距离,第i个节点存储DFS序为i的树结点到当前询问节点的距离(初始询问节点为1)(注意到求的是到叶子节点的最近距离,所以把非叶子节点的值设为INF

第二次DFS,

当DFS到节点x时,线段树中存储的距离恰好是询问节点x到各节点的距离,

对于每个询问,直接在线段树上查询区间最小值即可

对从x到儿子y,我们需要更新线段树的值,将x到各节点的距离改成y到各节点的距离

设x到y的距离为len,发现对于y的子树中的节点,距离会减少len,而对于其他节点,距离会增加len

由于DFS序的性质,y子树中的节点的DFS序是连续的一段,所以我们只要在线段树上进行区间更新即可

更新完之后继续DFS y节点,回溯时记得把线段树恢复

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define maxn 500005
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
vector<pair<int,int> >E[maxn];
int n,t;
int cnt=0;
int l[maxn],r[maxn];
long long dis[maxn];
void dfs1(int x,int fa){
l[x]=++cnt;
for(int i=0;i<E[x].size();i++){
int y=E[x][i].first;
if(y!=fa){
dis[y]=dis[x]+E[x][i].second;
dfs1(y,x);
}
}
r[x]=cnt;
} struct node{
int l;
int r;
long long mark;
long long v;
}tree[maxn<<2];
void push_up(int pos){
tree[pos].v=min(tree[pos<<1].v,tree[pos<<1|1].v);
}
void build(int l,int r,int pos){
tree[pos].l=l;
tree[pos].r=r;
if(l==r){
tree[pos].mark=tree[pos].v=0;
return;
}
int mid=(l+r)>>1;
build(l,mid,pos<<1);
build(mid+1,r,pos<<1|1);
push_up(pos);
}
void push_down(int pos){
if(tree[pos].mark){
tree[pos<<1].mark+=tree[pos].mark;
tree[pos<<1|1].mark+=tree[pos].mark;
tree[pos<<1].v+=tree[pos].mark;
tree[pos<<1|1].v+=tree[pos].mark;
tree[pos].mark=0;
}
}
void update(int L,int R,long long v,int pos){
if(L<=tree[pos].l&&R>=tree[pos].r){
tree[pos].v+=v;
tree[pos].mark+=v;
return;
}
push_down(pos);
int mid=(tree[pos].l+tree[pos].r)>>1;
if(L<=mid) update(L,R,v,pos<<1);
if(R>mid) update(L,R,v,pos<<1|1);
push_up(pos);
}
long long query(int L,int R,int pos){
if(L<=tree[pos].l&&R>=tree[pos].r){
return tree[pos].v;
}
push_down(pos);
int mid=(tree[pos].l+tree[pos].r)>>1;
long long ans=INF;
if(L<=mid) ans=min(ans,query(L,R,pos<<1));
if(R>mid) ans=min(ans,query(L,R,pos<<1|1));
return ans;
} struct range{
int l;
int r;
long long ans;
int id;
range(){ }
range(int L,int R,int i){
l=L;
r=R;
id=i;
ans=0;
}
void debug(){
printf("[%d,%d]",l,r);
}
};
vector<range>q[maxn]; void dfs2(int x,int fa){
for(int i=0;i<q[x].size();i++){//处理询问
q[x][i].ans=query(q[x][i].l,q[x][i].r,1);
}
for(int i=0;i<E[x].size();i++){
int y=E[x][i].first;
int len=E[x][i].second;
if(y!=fa){
update(l[y],r[y],-len,1);//更新距离
if(l[y]>1) update(1,l[y]-1,len,1);//注意边界条件
if(r[y]<n) update(r[y]+1,n,len,1);
dfs2(y,x);
update(l[y],r[y],len,1);//记得把线段树恢复成原状
if(l[y]>1) update(1,l[y]-1,-len,1);
if(r[y]<n) update(r[y]+1,n,-len,1);
}
}
} long long ans[maxn];
int main(){
int u,v,w,x,ll,rr;
scanf("%d %d",&n,&t);
for(int i=2;i<=n;i++){
scanf("%d %d",&v,&w);
E[v].push_back(make_pair(i,w));
E[i].push_back(make_pair(v,w));
}
for(int i=1;i<=n;i++){
sort(E[i].begin(),E[i].end());
}
for(int i=1;i<=t;i++){
scanf("%d %d %d",&x,&ll,&rr);
q[x].push_back(range(ll,rr,i));
}
dfs1(1,0);
build(1,n,1);
for(int i=1;i<=n;i++){
if(l[i]==r[i]) update(l[i],l[i],dis[i],1);//如果不是叶子节点,距离要设为INF
else update(l[i],l[i],INF,1);
}
dfs2(1,0);
for(int i=1;i<=n;i++){
for(int j=0;j<q[i].size();j++){//按照输入顺序输出
ans[q[i][j].id]=q[i][j].ans;
}
}
for(int i=1;i<=t;i++){
printf("%I64d\n",ans[i]);
}
}

Codeforces 1110F(DFS序+线段树)的更多相关文章

  1. CodeForces 877E DFS序+线段树

    CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...

  2. Codeforces 396C (DFS序+线段树)

    题面 传送门 题目大意: 给定一棵树,每个点都有权值,边的长度均为1,有两种操作 操作1:将节点u的值增加x,并且对于u的子树中的任意一个点v,将它的值增加x-dist(u,v)*k, dist(u, ...

  3. Codeforces 1132G(dfs序+线段树)

    题面 传送门 分析 对于每一个数a[i],找到它后面第一个大于它的数a[p],由p向i连边,最终我们就会得到一个森林,且p是i的父亲.为了方便操作,我们再增加一个虚拟节点n+1,把森林变成树. 由于序 ...

  4. Educational Codeforces Round 6 E dfs序+线段树

    题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili ...

  5. Codeforces 343D Water Tree(DFS序 + 线段树)

    题目大概说给一棵树,进行以下3个操作:把某结点为根的子树中各个结点值设为1.把某结点以及其各个祖先值设为0.询问某结点的值. 对于第一个操作就是经典的DFS序+线段树了.而对于第二个操作,考虑再维护一 ...

  6. Codeforces Round #442 (Div. 2)A,B,C,D,E(STL,dp,贪心,bfs,dfs序+线段树)

    A. Alex and broken contest time limit per test 2 seconds memory limit per test 256 megabytes input s ...

  7. CodeForces 877E Danil and a Part-time Job(dfs序+线段树)

    Danil decided to earn some money, so he had found a part-time job. The interview have went well, so ...

  8. 【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心

    3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 339  Solved: 130[Submit][Status][Discuss] D ...

  9. BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)

    题目这么说的: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: 输入小 ...

随机推荐

  1. Sql Server 2008安装时提示重启计算机失败解决办法

    在键盘上按下组合键[Win]+[R],调出运行窗口.   在窗口中输入“regedit”,点击确定,打开注册表管理界面.   在注册表左侧目录栏中找到如下位置:“HKEY_LOCAL_MACHINE\ ...

  2. wenzhang

    作者:周公子链接:https://zhuanlan.zhihu.com/p/94960418来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 2006年2月9日,首都经济 ...

  3. 关于Python获取图片文件二进制数据的问题(获取为空)

    在搭建fastdfs文件系统的时候遇到了点问题,在测试上传文件数据流的时候,需要Python来获取本地文件的二进制流 from fdfs_client.client import Fdfs_clien ...

  4. Redis复制实现原理

    摘要 我的前一篇文章<Redis 复制原理及特性>已经介绍了Redis复制相关特性,这篇文章主要在理解Redis复制相关源码的基础之上介绍Redis复制的实现原理. Redis复制实现原理 ...

  5. 00.斐波那契数列第n项

    # 斐波那契数列第n项 # 1 1 2 3 5 8 def fib(n): if n <= 2: return 1 else: return fib(n-2)+fib(n-1) def fib2 ...

  6. 雷哥带你了解webSocket原理

    偶然在知乎上看到一篇回帖,瞬间觉得之前看的那么多资料都不及这一篇回帖让我对 websocket 的认识深刻有木有.所以转到我博客里,分享一下.比较喜欢看这种博客,读起来很轻松,不枯燥,没有布道师的阵仗 ...

  7. jenkins解决python不是内部命令

    1.在 Windows 提示符下运行是没有问题. 2.把Jenkins项目配置中 python main.py   修改成python可执行文件全路径:D:\Python35\python.exe m ...

  8. php中美元符号是什么意思

    php中$符号是变量符号: 把$符号加上字符串,这个字符串就是一个变量名或对象名. 其实PHP采用的是C语言的语法,但是也有一些区别,$符号加上字符串,这就是一个变量名或对象名. 例如下面的代码:(推 ...

  9. QGIS SDK下载

    https://www.e-learn.cn/content/qita/1241748 基于OSGEO4W下载lib包. 并在VS中调用. 这样比直接编译容易一些.可以快速上手.

  10. 28 August

    单调队列复习. 投资 (invest) 给定一带符号整数数列,求长度为 \([s, e]\) 的子区间的和的最大值.(最大子段和) 降二维为一维,for循环枚举区间右端点.预处理前缀和,问题转化为找到 ...