树链剖分+线段树模板

模板题链接:https://www.luogu.com.cn/problem/P3384

这个树剖的知识点不难,只要理解了什么是重链,怎么把一棵树重新编号,让它满足建成一颗线段树的线性条件,那么就很容易了,我打了两边,第一遍那个查询区域和修改区域的地方错了,第二次也是这块区域出了点小问题,还有线段树更新里面最后那里忘记重新计算和了。。

下面是我第一次打的模板,嗯~整体结构一般吧。。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 200010;
inline ll read(){

    ll x = 0,f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = (x<<3) + (x<<1) + (ch ^ '0'); ch = getchar();}
    return x * f;
}
/*-----------------------------------以上预处理-----------------------------------*/

struct edge{    //链式向前星
    int to,next;
}edges[maxn];

int head[maxn],cnt1;    //  链式向前星用到的变量

void add_edge(int u,int v){
    edges[++cnt1].next = head[u];
    edges[cnt1].to = v;
    head[u] = cnt1;
}

int top[maxn],fa[maxn],dep[maxn],son[maxn],siz[maxn],id[maxn],cnt2;    //每条链的顶点,父节点,深度,重儿子,还有dfs2新编号的id,用到的编号变量
ll w[maxn],wt[maxn];   //原序号权值,新编号的权值
int N,M,R,mod;    //节点数,边数,根,模

struct Node{
    int l,r;
    ll add,sum;
}nodes[maxn<<1];
/*-----------------------------------以上数据定义-----------------------------------*/

void build(int l,int r,int p){

    nodes[p].l = l, nodes[p].r = r;

    if(l == r){
        nodes[p].sum = wt[l] % mod;
        return;
    }   

    int mid = (l+r) >> 1;

    build(l,mid,p<<1);
    build(mid+1,r,p<<1|1);

    nodes[p].sum = (nodes[p<<1].sum + nodes[p<<1|1].sum) % mod;
}


void push_down(int p){

    nodes[p<<1].add = (nodes[p<<1].add + nodes[p].add) % mod;
    nodes[p<<1|1].add = (nodes[p<<1|1].add + nodes[p].add) % mod;

    nodes[p<<1].sum = (nodes[p<<1].sum + nodes[p].add * (nodes[p<<1].r - nodes[p<<1].l + 1)) % mod;
    nodes[p<<1|1].sum = (nodes[p<<1|1].sum + nodes[p].add * (nodes[p<<1|1].r - nodes[p<<1|1].l + 1)) % mod;

    nodes[p].add = 0;
}

void add(int l,int r,ll k,int p){

    if(l <= nodes[p].l && nodes[p].r <= r){
        nodes[p].sum = (nodes[p].sum + k * (nodes[p].r - nodes[p].l + 1)) % mod;
        nodes[p].add = (nodes[p].add + k);
        return;
    }

    if(nodes[p].add) push_down(p);

    int mid = (nodes[p].l + nodes[p].r) >> 1;
    if(l <= mid) add(l,r,k,p<<1);
    if(r > mid) add(l,r,k,p<<1|1);

    nodes[p].sum = (nodes[p<<1].sum + nodes[p<<1|1].sum) % mod;
}

ll query(int l,int r,int p){

    if(l <= nodes[p].l && nodes[p].r <= r){
        return nodes[p].sum;
    }

    push_down(p);

    ll ans = 0;
    int mid = (nodes[p].l + nodes[p].r) >> 1;

    if(l <= mid) ans += query(l,r,p<<1);
    if(r > mid) ans += query(l,r,p<<1|1);

    return ans % mod;
}

/*-----------------------------------以上线段树-----------------------------------*/


void dfs1(int x,int f, int deep){

    dep[x] = deep;
    fa[x] = f;
    siz[x] = 1;
    int maxson = -1;

    for(int i = head[x]; i; i = edges[i].next){

        int to = edges[i].to;
        if(to == f) continue;
        dfs1(to,x,deep+1);
        siz[x] += siz[to];
        if(maxson < siz[to]){maxson = siz[to]; son[x] = to;}
    }
}

void dfs2(int x,int topf){
    
    id[x] = ++cnt2;
    wt[cnt2] = w[x]; 
    top[x] = topf;
    if(!son[x]) return;
    dfs2(son[x],topf);

    for(int i = head[x]; i; i = edges[i].next){

        int to = edges[i].to;
        if(to == fa[x] || to == son[x]) continue;
        dfs2(to,to);
    }
}

ll query_range(int x,int y){

    ll ans = 0;

    while(top[x] != top[y]){

        if(dep[top[x]] < dep[top[y]]) swap(x,y);

        ans = (ans + query(id[top[x]],id[x],1)) % mod;

        x = fa[top[x]];
    }

    if(dep[x] > dep[y]) swap(x,y);

    ans =  (ans + query(id[x],id[y],1))%mod;

    return ans;
}

void add_range(int x,int y,ll k){

    while(top[x] != top[y]){

        if(dep[top[x]] < dep[top[y]]) swap(x,y);

        add(id[top[x]],id[x],k,1);

        x = fa[top[x]];
    }

    if(dep[x] > dep[y]) swap(x,y);

    add(id[x], id[y],k, 1);
}

ll query_son(int x){

    return query(id[x],id[x] + siz[x] - 1,1);
}

void add_son(int x,int k){
    
    add(id[x],id[x] + siz[x] - 1,k,1);
}

int main(){

    N = read(),M = read(),R = read(),mod = read();

    for(int i = 1;i<=N;i++){
        w[i] = read();
    }
    int u,v;
    for(int i = 1;i<N;i++){
        u = read(),v = read();
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs1(R,0,1);
    dfs2(R,R);
    build(1,N,1);
    int op,x,y;
    ll z;
    for(int i = 1; i<=M; i++){
        op = read();
        if(op == 1){
            x = read(),y = read(),z = read();
            add_range(x,y,z);
        }
        else if(op == 2){
            x = read(), y = read();

            printf("%lld\n",query_range(x,y));
        }
        else if(op == 3){
            x = read(), z = read();
            add_son(x,z);
        }else{
            x = read();

            printf("%lld\n",query_son(x));
        }
    }


    // system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值