Codeforces 1110F(DFS序+线段树)

题面

传送门

分析

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]);
    }
}

原文地址:https://www.cnblogs.com/birchtree/p/10357194.html

时间: 2024-11-09 00:50:53

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

Educational Codeforces Round 6 E dfs序+线段树

题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili上电子科技大学发的视频学习的 将一颗树通过dfs编号的方式 使每个点的子树的编号连在一起作为相连的区间 就可以配合线段树搞子树 因为以前好像听说过 线段树可以解决一种区间修改和查询区间中不同的xx个数...所以一下子就想到了... 但是我不会写线段树..只会最简单的单点修改区间查询...不会用延迟标

codeforces 343D Water Tree 树链剖分 dfs序 线段树 set

题目链接 这道题主要是要考虑到同一棵子树中dfs序是连续的 然后我就直接上树剖了... 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=600005; 4 5 struct Node 6 { 7 int l,r; 8 int value; 9 void init() 10 { 11 l=r=value=0; 12 } 13 }tree[4*MAXN]; 14 vector<int>nei[MAXN]

POJ 3321 DFS序+线段树

单点修改树中某个节点,查询子树的性质.DFS序 子树序列一定在父节点的DFS序列之内,所以可以用线段树维护. 1: /* 2: DFS序 +线段树 3: */ 4:   5: #include <cstdio> 6: #include <cstring> 7: #include <cctype> 8: #include <algorithm> 9: #include <vector> 10: #include <iostream> 1

codevs1228 (dfs序+线段树)

总结: 第一次遇到dfs序的问题,对于一颗树,记录节点 i 开始搜索的序号 Left[i] 和结束搜索的序号 Righti[i],那么序号在 Left[i] ~ Right[i] 之间的都是节点 i 子树上的节点. 并且此序号与线段树中 L~R 区间对应,在纸上模拟了几遍确实如此,但暂时还未理解为何对应. 此题就是dfs序+线段树的裸题 代码: #include<iostream> #include<vector> #include<cstring> #include&

[BZOJ 3306]树(dfs序+线段树+倍增)

Description 给定一棵大小为 n 的有根点权树,支持以下操作: • 换根 • 修改点权 • 查询子树最小值 Solution 单点修改子树查询的话可以想到用dfs序+线段树来处理,换根的处理画一画图应该可以明白: 如果查询的x是当前的根rt,直接返回整棵树的min 如果rt在x的子树中,用倍增的方法找到离x最近的rt的祖先t,整棵树除t的子树以外的部分就是x当前根下的子树 如果rt不在x的子树中,查询x原来的子树的min值 #include<iostream> #include<

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

3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 339  Solved: 130[Submit][Status][Discuss] Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏. 今天他得到了一款新游戏<XX半岛>,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景.所有场景和选择支构成树状结构:开始游戏时在根节点(共通线)

【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树

题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\),问你答案是多少. \(n\leq {10}^5,k\leq {10}^9\) 题解 设\(l\)为这棵树的叶子个数,显然当\(k>\)树的深度时答案都是\(l\). 下面要证明:答案是\(O(l+\frac{n-l}{k})\)的. 我们从下往上贪心,每次选择一个未被覆盖的深度最深的点,覆盖这个点网

Manthan, Codefest 16(G. Yash And Trees(dfs序+线段树))

题目链接:点击打开链接 题意:给你一棵树, 根结点为1, q组操作, 每组操作有两种, 一种是对一个结点的所有子树结点的值全部+1, 另一种是查询一个结点的子树结点上值%m的余数为素数的个数. 思路:对于第一个操作, 我们可以想到用dfs序给树重新标号, 使得一个结点的子树结点为相邻的一条线段, 这样,就可以很容易的用线段树进行处理了.  对于第二个操作, 为了维护一个区间内的值, 我们可以用bitset作为结点信息.  我们可以开一个m位的bitset, 对于每个位, 1表示这个数在此区间中,

CodeForces 838B - Diverging Directions - [DFS序+线段树]

题目链接:http://codeforces.com/problemset/problem/838/B You are given a directed weighted graph with n nodes and 2n?-?2 edges. The nodes are labeled from 1 to n, while the edges are labeled from 1 to 2n?-?2. The graph's edges can be split into two parts.