BZOJ4034 树上操作(树剖 线段树大模板)

BZOJ4034

long long 是大坑点

貌似long long 跟int 乘起来会搞事情?...

A了这题线段树和树剖的基础OK

嘛 重点过掉的还是线段树区间更新的lazy tag吧

#include<cstdio>
#include<cstring>
#define N 100001
using namespace std;

struct ed{
    int nxt,to;
}e[N*2];
int ne=0,head[N];
long long int w0[N];

struct t{
    int l,r;
    long long sum,add;
}tree[N*4];
int father[N],son[N],hson[N],id[N],reid[N],top[N],cnt=0;
int deep[N];
int n,m;

void init1(int cur,int fat,int d)
{
    deep[cur]=d;
    father[cur]=fat;
    son[cur]=0;

    for(int i=head[cur];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(v==fat) continue;
        init1(v,cur,d+1);
        son[cur]+=1+son[v];
        if(son[v]>=son[hson[cur]]) hson[cur]=v;
    }
}

void init2(int cur,int up)
{
    id[cur]=++cnt;
    reid[cnt]=cur;
    top[cur]=up;

    if(hson[cur])
        init2(hson[cur],up);

    for(int i=head[cur];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(v==hson[cur]||v==father[cur]) continue;
        init2(v,v);
    }
}

void build(int L,int R,int node)
{
    tree[node].l=L;
    tree[node].r=R;

    if(L==R)
    {
        tree[node].sum=w0[reid[L]];
        return ;
    }

    int mid=(L+R)>>1;

    build(L,mid,node*2);
    build(mid+1,R,node*2+1);

    tree[node].sum=tree[node*2].sum+tree[node*2+1].sum;
}

void pushdown(int node)
{
    long long int k=tree[node].add;
    tree[node].add=0;
    node=node*2;

    tree[node].add+=k;
    tree[node+1].add+=k;
    tree[node].sum+=k*(tree[node].r-tree[node].l+1);
    tree[node+1].sum+=k*(tree[node+1].r-tree[node+1].l+1);
}

void update(int L,int R,long long int val,int node)
{
    if(L>tree[node].r || R<tree[node].l) return; 

    if(L<=tree[node].l && R>=tree[node].r)
    {
        tree[node].sum+=(tree[node].r-tree[node].l+1)*val;
        tree[node].add+=val;
        return;
    }

    if(tree[node].add) pushdown(node);
    update(L,R,val,node*2);
    update(L,R,val,node*2+1);
    tree[node].sum=tree[node*2].sum+tree[node*2+1].sum;
}

long long int getsum(int L,int R,int node)
{
    if(L>tree[node].r || R<tree[node].l) return 0;

    if(L<=tree[node].l && R>=tree[node].r)
        return tree[node].sum;

    pushdown(node);
    return getsum(L,R,node*2)+getsum(L,R,node*2+1);
}

long long int Qsum(int v)
{
    long long int ans=0;
    while(top[v]!=1)
    {
        ans+=getsum(id[top[v]],id[v],1);
        v=father[top[v]];
    }

    ans+=getsum(1,id[v],1);

    return ans;
}

void ade(int from,int to)
{
    e[++ne].nxt=head[from];
    head[from]=ne;
    e[ne].to=to;
}

int main()
{
    scanf("%d %d",&n,&m);

     memset(tree,0,sizeof(tree));

    for(int i=1;i<=n;i++)
        scanf("%lld",&w0[i]);

    for(int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        ade(a,b);
        ade(b,a);
    }

    init1(1,0,1);
    init2(1,1);
    build(1,cnt,1);

    for(int i=1;i<=m;i++)
    {
        int flag;
        scanf("%d",&flag);

        if(flag==1)
        {
            int pt;
            long long val;//val这里是大迷点
            scanf("%d %lld",&pt,&val);
            update(id[pt],id[pt],val,1);
        }
        else if(flag==2)
        {
            int root;
            long long int val;
            scanf("%d %lld",&root,&val);
            update(id[root],id[root]+son[root],val,1);
        }
        else
        {
            int v;
            scanf("%d",&v);
            printf("%lld\n",Qsum(v));
        }
    }

    return 0;
}
时间: 2024-12-10 23:27:49

BZOJ4034 树上操作(树剖 线段树大模板)的相关文章

POJ3417Network(LCA+树上查分||树剖+线段树)

Yixght is a manager of the company called SzqNetwork(SN). Now she's very worried because she has just received a bad news which denotes that DxtNetwork(DN), the SN's business rival, intents to attack the network of SN. More unfortunately, the origina

[GX/GZOI2019]旧词(树上差分+树剖+线段树)

考虑k=1的做法:这是一道原题,我还写过题解,其实挺水的,但当时我菜还是看题解的:https://www.cnblogs.com/hfctf0210/p/10187947.html.其实就是树上差分后值为1. 考虑k>1的做法:其实可以再次树上差分,给每个点i赋值v[i]=dep[i]k-dep[i-1]k,然后还是和原来一样开一棵线段树,记录一个val[rt]表示当前节点内区间v值的和,以及sum[rt]表示区间值.修改时打标记,只需要将sum[rt]+=v*val[rt],lazy[rt]+

poj3728The merchant树剖+线段树

如果直接在一条直线上,那么就建线段树 考虑每一个区间维护最小值和最大值和答案,就符合了合并的条件,一个log轻松做 那么在树上只要套一个树剖就搞定了,多一个log也不是问题 注意考虑在树上的话每一条链都有可能是正着被用和反着被用,所以存两个答案 所以维护信息只需要一个merge和一个reverse 代码如下: 1 #include <cstdio> 2 #include <iostream> 3 #define mid (l+r>>1) 4 using namespac

bzoj 5210: 最大连通子块和【动态dp+树剖+线段树+堆】

参考:https://www.cnblogs.com/CQzhangyu/p/8632904.html 要开longlong的 首先看dp,设f[u]为必选u点的子树内最大联通块,p[u]为不一定选u的子树内最大联通块,转移很显然就是f[u]=max(Σf[v],0),p[u]=max(max(p[v]),f[u]) 然后看动态的部分,设g是不算重儿子的f,然后每条链上的真实f值要用一棵线段树维护g来得到,具体形式是f[u]=max(g[v]+f[hs[v]],0),是一个最长连续子序列的形式,

BZOJ 3196 二逼平衡树 树套树(线段树套Treap)

题目大意: 写一种数据结构,他可以: 1.查询k在区间内的排名. 2.查询区间内排名为k的值 3.修改某一个值. 4.求k在区间内的前驱. 5.求k在区间内的后继. 思路:本来以为有什么只有神犇才知道的神一般的数据结构来维护它,问了别人之后,发现只是树套树.据说怎么套都行.我见识鄙陋,就只能线段树套Treap了. 这也是第一次写树套树,还1A了,有点开心. 写树套树,一定要确定自己对这两个树及其熟练,加上少量精细的思考,就可以完成树套树.(我只是弱渣,求神犇别D) 具体实现:第一层是线段树,第二

区间树和线段树

注意:区间树和线段树不一样哦,线段树是一种特殊的区间树. 区间树: 区间树是在红黑树基础上进行扩展得到的支持以区间为元素的动态集合的操作,其中每个节点的关键值是区间的左端点.通过建立这种特定的结构,可是使区间的元素的查找和插入都可以在O(lgn)的时间内完成.相比于基础的红黑树数据结构,增加了一个max[x],即以x为根的子树中所有区间的断点的最大值.逻辑结构如下所示: 区间树具有和红黑树一样的性质,并且区间树的基础操作和红黑树的基础操作一样.(具体红黑树的操作,参见http://blog.cs

主席树/函数式线段树/可持久化线段树

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

[补档计划] 树4 - 线段树

[CF787D] Legacy 题意 $N$ 个点, $M$ 条连边操作: $1~u~v~w~:~u\rightarrow v$ . $2~u~l~r~w~:~u\rightarrow [l, r]$ $3~u~l~r~w~:~[l, r]\rightarrow u$ . 给定点 $s$ , 求单源最短路. $N\le 10^5$ . 分析 考虑使用 线段树结构 优化 建图. 建立一棵线段树, 每个点表示一个区间. 拆点, 线段树的每个点拆成 入点 和 出点 . 出线段树 的儿子连父亲, 因为可

【vijos】1750 建房子(线段树套线段树+前缀和)

https://vijos.org/p/1750 是不是我想复杂了.... 自己yy了个二维线段树,然后愉快的敲打. 但是wa了两法.......sad 原因是在处理第二维的更新出现了个小问题,sad. void pushup1(int x) { for1(i, 1, mm<<2) mn[x][i]=min(mn[lc][i], mn[rc][i]); } 这里注意是mm*4...我该好好想想了..这是在dbg的时候找出来的问题.sad. 我觉得很奇怪,线段树的底层节点一共就mm个,那么整棵树