Luogu P2982 [USACO10FEB]慢下来 Slowing down | dfs序、线段树

题目链接

题目大意:

有一棵N个结点树和N头奶牛,一开始所有奶牛都在一号结点,奶牛们将按从编号1到编号N的顺序依次前往自己的目的地,求每头奶牛在去往自己目的地的途中将会经过多少已经有奶牛的结点。

题解:

可以发现,每一头奶牛到达目的地后,都只会对还未到达目的地的奶牛中,目的地在它目的地子树中的奶牛的答案产生贡献。

比如说在下面这棵树中,一头奶牛到达了图中深色结点,那么在还未到达目的地的奶牛中,只有目的地在深色结点子树中的奶牛才会由深色结点对答案产生贡献。

所以,我们可以在每头奶牛到达目的地后,将其目的地所在结点的子树中每一个结点的权值都加上一,询问时输出该奶牛目的地所在结点的权值即可。

由于每一次的修改操作都是在一棵子树内进行的,所以考虑用dfs序给结点编号(因为每棵子树中结点的dfs序都一定是连续的一段),再用线段树进行维护就好。

代码:

#include<iostream>
#include<cstdio>
    using namespace std;
struct edge
{
    int last;
    int end;
}e[200005];
    int ne=0,idx=0,dfn[100005],note[100005],size[100005],tree[400005],lazy[400005];
void make_edge(int u,int v)
{
    ne++;
    e[ne].last=note[u];
    e[ne].end=v;
    note[u]=ne;
}
void dfs(int x,int fx)
{
    dfn[x]=++idx;//用dfs序给结点编号
    size[x]=1;
    for(int i=note[x];i;i=e[i].last)
        if(e[i].end!=fx)
        {
            dfs(e[i].end,x);//继续dfs
            //计算每个结点的子树大小,用于计算此结点的子树中最大的dfs序是多少,便于操作
            size[x]+=size[e[i].end];
        }
}
//线段树板子
void add_node(int p,int l,int r,int k)
{
    tree[p]+=(r-l+1)*k;
    lazy[p]+=k;
}
void clean_lazy(int p,int l,int r)
{
    int mid=(l+r)>>1;
    add_node((p<<1),l,mid,lazy[p]);
    add_node((p<<1)+1,mid+1,r,lazy[p]);
    lazy[p]=0;
}
void add(int p,int l,int r,int x,int y)
{
    if(x>y) return;
    if(l==x&&r==y)
    {
        add_node(p,l,r,1);
        return;
    }
    clean_lazy(p,l,r);
    int mid=(l+r)>>1;
    if(y<=mid) add((p<<1),l,mid,x,y);
    else if(x>mid) add((p<<1)+1,mid+1,r,x,y);
    else add((p<<1),l,mid,x,mid),add((p<<1)+1,mid+1,r,mid+1,y);
}
int ask(int p,int l,int r,int x)
{
    if(l==r) return tree[p];
    clean_lazy(p,l,r);
    int mid=(l+r)>>1;
    if(x<=mid) return ask((p<<1),l,mid,x);
    return ask((p<<1)+1,mid+1,r,x);
}
int main()
{
    int n=0;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int u=0,v=0;
        scanf("%d%d",&u,&v);
        make_edge(u,v);
        make_edge(v,u);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)
    {
        int x=0;
        scanf("%d",&x);
        printf("%d\n",ask(1,1,n,dfn[x]));//直接输出目的地所在的权值
        add(1,1,n,dfn[x]+1,dfn[x]+size[x]-1);//给当前目的地所在结点的子树中所有结点的权值都加1
    }
    return 0;
}

原文地址:https://www.cnblogs.com/wozaixuexi/p/9460077.html

时间: 2024-10-15 20:16:45

Luogu P2982 [USACO10FEB]慢下来 Slowing down | dfs序、线段树的相关文章

luogu 2982 [USACO10FEB]慢下来Slowing down dfs序+树状数组

刷水~ Code: #include <bits/stdc++.h> #define N 100004 #define setIO(s) freopen(s".in","r",stdin) using namespace std; int n,edges,tim; int hd[N],to[N<<1],nex[N<<1],st[N],ed[N],C[N]; void add(int u,int v) { nex[++edges]=

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<

Educational Codeforces Round 6 E dfs序+线段树

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

【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})\)的. 我们从下往上贪心,每次选择一个未被覆盖的深度最深的点,覆盖这个点网

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]

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

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