bzoj 4034(DFS序+线段树)

这个题多了一个操作难度直线上升,看完题解才会写

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

按照题意:记录其DFS序,然后进栈是正,出栈为负,利用线段树进行更新

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
const int N = 800005;
struct Edge
{
    int v,next;
} edge[N<<1];
int head[N],tot,cnt;
int val[N];
int in[N],out[N];
LL tree[N<<2],lazy[N<<2],seg[N];
int flag[N<<2];
int io[N];
void addEdge(int u,int v)
{
    edge[tot].v = v,edge[tot].next = head[u],head[u] = tot++;
}
void init()
{
    memset(head,-1,sizeof(head));
    memset(val,0,sizeof(val));
    memset(lazy,0,sizeof(lazy));
    memset(io,0,sizeof(io));
    memset(seg,0,sizeof(seg));
    tot = cnt = 0;
}
void dfs(int u,int fa)
{
    seg[in[u] = ++cnt] = (LL)val[u];
    io[cnt] = 1;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        if(edge[i].v != fa)
        {
            dfs(edge[i].v,u);
        }
    }
    seg[out[u] = ++cnt] = (LL)(-val[u]);
    io[cnt] = -1;
}

void pushup(int idx)
{
    tree[idx] = tree[idx<<1]+tree[idx<<1|1];
    return;
}
void pushdown(int idx)
{
    if(lazy[idx])
    {
        tree[idx << 1] +=  flag[idx<<1]*lazy[idx];
        tree[idx << 1 | 1] += flag[idx<<1|1]*lazy[idx];
        lazy[idx << 1] += lazy[idx];
        lazy[idx << 1 | 1] += lazy[idx];
        lazy[idx] = 0;
    }
    return;
}
void build(int l,int r,int idx)
{
    if(l==r)
    {
        tree[idx] = seg[l];
        if(io[l]>0) flag[idx] = 1;
        else flag[idx] = -1;
        return;
    }
    int mid = (l+r)>>1;
    build(l,mid,idx<<1);
    build(mid+1,r,idx<<1|1);
    pushup(idx);
    flag[idx] = flag[idx<<1] + flag[idx<<1|1];
}
void update(int l,int r,int L,int R,int idx,int val)
{
    if(l>=L&&r<=R)
    {
        tree[idx] =tree[idx] + (LL)flag[idx]*val;
        lazy[idx] =lazy[idx] + (LL)val;
        return;
    }
    int mid = (l+r)>>1;
    pushdown(idx);
    if(mid>=L) update(l,mid,L,R,idx<<1,val);
    if(mid<R) update(mid+1,r,L,R,idx<<1|1,val);
    pushup(idx);
}
LL query(int l,int r,int L,int R,int idx){
    if(l>=L&&r<=R)
    {
        return tree[idx];
    }
    int mid = (l+r)>>1;
    LL sum = 0;
    pushdown(idx);
    if(mid>=L) sum+=query(l,mid,L,R,idx<<1);
    if(mid<R) sum+=query(mid+1,r,L,R,idx<<1|1);
    return sum;
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&val[i]);
        }
        for(int i=1; i<n; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            addEdge(u,v);
            addEdge(v,u);
        }
        dfs(1,0);
        build(1,2*n,1);
        while(m--)
        {
            int op,x,y;
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d%d",&x,&y);
                update(1,2*n,in[x],in[x],1,y);
                update(1,2*n,out[x],out[x],1,y);
            }else if(op==2){
                scanf("%d%d",&x,&y);
                update(1,2*n,in[x],out[x],1,y);
            }else{
                scanf("%d",&x);
                LL sum = query(1,2*n,1,in[x],1);
                printf("%lld\n",sum);
            }
        }
    }
}
时间: 2024-10-06 02:50:38

bzoj 4034(DFS序+线段树)的相关文章

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

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

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&

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表示这个数在此区间中,

BZOJ.4817.[SDOI2017]树点涂色(LCT DFS序 线段树)

题目链接 1.2裸树剖,但是3.每个点的答案val很不好维护.. 如果我们把同种颜色的点划分到同一连通块中,那么向根染色的过程就是Access()! 最初所有点间都是虚边,相同颜色点用实边相连.一条边由实边变为虚边时,深度大的点所在子树所有点val+1(Access()中原先x的右儿子答案+1,因为x颜色变了): 由虚边变为实边时,深度大的点所在子树所有点val-1(fa[x]颜色与x相同导致fa[x]的贡献没了).(其实是因为 实链数量(贡献)就等于虚边数量+1?无所谓了) 于是2.就是val