树链剖分

太久没写过板子都有点不熟了,今天打一下发现还是有些小问题,水篇blog警醒一下。

修改链、子树,查询链、子树和。

#include<iostream>
#include<cstdio>
using namespace std;

const int N = 100005;

int n, m, r, p;

int TR[N*4], tag[N*4], a[N], qwq[N];

int tov[N*2], nex[N*2], h[N], stot;

void add ( int u, int v ) {
    tov[++stot] = v;
    nex[stot] = h[u];
    h[u] = stot;
}

void update ( int nd ) {
    TR[nd] = ( TR[nd << 1] + TR[nd << 1 | 1] ) % p;
}

void push_down ( int nd, int l, int r ) {
    if ( tag[nd] ) {
        int mid = ( l + r ) >> 1;
        TR[nd << 1] = ( TR[nd << 1] + tag[nd] * ( mid - l + 1 ) % p ) % p;
        TR[nd << 1 | 1] = ( TR[nd << 1 | 1] + tag[nd] * ( r - mid ) % p ) % p;
        tag[nd << 1] = ( tag[nd << 1] + tag[nd] ) % p;
        tag[nd << 1 | 1] = ( tag[nd << 1 | 1] + tag[nd] ) % p;
        tag[nd] = 0;
    }
}

void build ( int nd, int l, int r ) {
    if ( l == r ) {
        TR[nd] = qwq[l]; ///////////////////////
        return ;
    }
    int mid = ( l + r ) >> 1;
    build ( nd << 1, l, mid );
    build ( nd << 1 | 1, mid + 1, r );
    update ( nd ); ///
}

int fa[N], siz[N], dep[N], son[N];

void dfs1 ( int u, int f ) {
    fa[u] = f;
    siz[u] = 1;
    dep[u] = dep[f] + 1;
    for ( int i = h[u]; i; i = nex[i] ) {
        int v = tov[i];
        if ( v == f ) continue;
        dfs1 ( v, u );
        siz[u] += siz[v];
        if ( siz[v] > siz[son[u]] ) son[u] = v;
    }
}

int in[N], top[N], ti, out[N];

void dfs2 ( int u, int tp ) {
    top[u] = tp;
    in[u] = ++ ti;
    qwq[ti] = a[u]; ///////////////////////////
    if ( son[u] ) dfs2 ( son[u], tp );
    for ( int i = h[u]; i; i = nex[i] ) {
        int v = tov[i];
        if ( v == fa[u] || v == son[u] ) continue;
        dfs2 ( v, v );
    }
    out[u] = ti; //////////////////////////
}

void add ( int nd, int L, int R, int l, int r, int delta ) {
    if ( l >= L && r <= R ) {
        TR[nd] = ( TR[nd] + delta * ( r - l + 1 ) % p ) % p; ////////////
        tag[nd] = ( tag[nd] + delta ) % p;
        return ;
    }
    push_down ( nd, l, r );
    int mid = ( l + r ) >> 1;
    if ( L <= mid ) add ( nd << 1, L, R, l, mid, delta );
    if ( R > mid ) add ( nd << 1 | 1, L, R, mid + 1, r, delta );
    update ( nd );
}

void add ( int u, int v, int delta ) {
    while ( top[u] != top[v] ) {
        if ( dep[top[u]] < dep[top[v]] ) swap ( u, v );
        add ( 1, in[top[u]], in[u], 1, n, delta );
        u = fa[top[u]];
    }
    if ( dep[u] < dep[v] ) swap ( u, v );
    add ( 1, in[v], in[u], 1, n, delta );
}

int query ( int nd, int L, int R, int l, int r ) {
    if ( l >= L && r <= R ) return TR[nd];
    push_down ( nd, l, r );
    int ans = 0;
    int mid = ( l + r ) >> 1;
    if ( L <= mid ) ans = ( ans + query ( nd << 1, L, R, l, mid ) ) % p;
    if ( R > mid ) ans = ( ans + query ( nd << 1 | 1, L, R, mid + 1, r ) ) % p;
    return ans;
}

int query ( int u, int v ) {
    int ans = 0;
    while ( top[u] != top[v] ) {
        if ( dep[top[u]] < dep[top[v]] ) swap ( u, v );
        ans = ( ans + query ( 1, in[top[u]], in[u], 1, n ) ) % p;
        u = fa[top[u]];
    }
    if ( dep[u] < dep[v] ) swap ( u, v );
    ans = ( ans + query ( 1, in[v], in[u], 1, n ) ) % p;
    return ans;
}

void add_ ( int u, int delta ) {
    add ( 1, in[u], out[u], 1, n, delta );
}

int query_ ( int u ) {
    return query ( 1, in[u], out[u], 1, n );
}

int main ( ) {
    scanf ( "%d%d%d%d", &n, &m, &r, &p );
    for ( int i = 1; i <= n; i ++ )
        scanf ( "%d", &a[i] );
    for ( int i = 1; i < n; i ++ ) {
        int x, y;
        scanf ( "%d%d", &x, &y );
        add ( x, y );
        add ( y, x );
    }
    dfs1 ( r, r );
    dfs2 ( r, r );
    build ( 1, 1, n ); /////线段树根节点是1
    for ( int i = 1; i <= m; i ++ ) {
        int opt;
        scanf ( "%d", &opt );
        if ( opt == 1 ) {
            int x, y, z;
            scanf ( "%d%d%d", &x, &y, &z );
            add ( x, y, z );
        } else if ( opt == 2 ) {
            int x, y;
            scanf ( "%d%d", &x, &y );
            printf ( "%d\n", query ( x, y ) );
        } else if ( opt == 3 ) {
            int x, y;
            scanf ( "%d%d", &x, &y );
            add_ ( x, y );
        } else {
            int x;
            scanf ( "%d", &x );
            printf ( "%d\n", query_ ( x ) );
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值