【BZOJ 3589】 动态树

思路

维护点到跟的路径的权值和,记录二元组 (a,b) 来处理子树加的情况,多条路径的信息合并时,枚举每一条边与其他边的相交的情况,求 LCA 分类讨论即可。
时间复杂度: O(k2×qlogn)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef unsigned int uint;
const int maxn = 200010;
const uint mod = (1u<<31)-1;

int n, m;
uint head[maxn], to[maxn<<1], nxt[maxn<<1], cnt;
uint top[maxn], dep[maxn], fa[maxn], son[maxn], st[maxn], tp;
uint num[maxn], pos[maxn], tim, q[maxn], sz[maxn];
void add(int a, int b){nxt[++ cnt] = head[a], to[head[a] = cnt] = b;}
#define mid ((l+r)>>1)
#define lch ((now<<1))
#define rch ((now<<1)|1)
struct node{
    uint x, y, tg1, tg2;
    node(uint a = 0, uint b = 0){x = a, y = b, tg1 = 0, tg2 = 0;}
    void update(uint a, uint b){x += a, y += b, tg1 += a, tg2 += b;}
    void down();
}T[maxn*3];
void node::down(){
    uint now = (this - T);
    T[lch].update(tg1, tg2);
    T[rch].update(tg1, tg2);
    tg1 = 0, tg2 = 0;
}
void pre(){
    uint l = 1, r = 0; tp = 0;
    q[++ r] = 1, dep[1] = 1;
    while(l <= r){
        uint x = q[l ++]; sz[x] = 1;
        for(int i = head[x]; i; i = nxt[i]){
            uint u = to[i]; if(u == fa[x]) continue;
            dep[u] = dep[x] + 1, fa[u] = x, q[++ r] = u;
        }
    }
    for(int i = n; i >= 1; i --){
        uint x = q[i]; if(!fa[x]) continue;
        sz[fa[x]] += sz[x];
        if(sz[son[fa[x]]] < sz[x]) son[fa[x]] = x;
    }
    st[++ tp] = 1, top[1] = 1;
    while(tp){
        uint x = st[tp --]; pos[x] = ++ tim, num[tim] = x;
        for(int i = head[x]; i; i = nxt[i]){
            uint u = to[i]; if(u == fa[x] || u == son[x]) continue;
            st[++ tp] = u, top[u]  = u;
        }
        if(son[x]) st[++ tp] = son[x], top[son[x]] = top[x];
    }
}

int lca(int x, int y){
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]]) y = fa[top[y]];
        else x = fa[top[x]];
    } return dep[x] < dep[y] ? x : y;
}
void modify(int now, int l, int r, int pos1, int pos2, uint a, uint b){
    if(l == pos1 && r == pos2){T[now].update(a, b); return;}T[now].down();
    if(pos2 <= mid) modify(lch, l, mid, pos1, pos2, a, b);
    else if(pos1 >= mid+1) modify(rch, mid+1, r, pos1, pos2, a, b);
    else modify(lch, l, mid, pos1, mid, a, b), modify(rch, mid+1, r, mid+1, pos2, a, b);
}

uint que(int now, int l, int r, int pos){
    if(l == r) return T[now].x*dep[num[l]] + T[now].y; T[now].down();
    if(pos <= mid) return que(lch, l, mid, pos);
    else return que(rch, mid+1, r, pos);
}
struct point{int x, y;};
inline int gt(){  
    char _ch; int _num = 0, _ok = 0;  
    while(1){  
        _ch = getchar();  
        if(_ch >= '0' && _ch <= '9') _num = _num*10 + _ch - '0', _ok = 1;  
        else if(_ok) return _num;  
    }  
}  
int main(){
    scanf("%d", &n);
    for(int i = 1, a, b; i < n; i ++){
        a = gt(), b = gt();
        add(a, b), add(b, a);
    }
    pre();
    scanf("%d", &m);
    for(int t = 1; t <= m; t ++){
        int op = gt(), x, y;
        if(op == 0){
            x = gt(), y = gt();
            modify(1, 1, n, pos[x], pos[x]+sz[x]-1, y, uint(-y * (dep[x]-1)));
        }else{
            int k = gt(); 
            uint sum = 0; 
            point p[6];
            for(int i = 1; i <= k; i ++){
                p[i].x = gt(), p[i].y = gt();
                if(dep[p[i].x] < dep[p[i].y]) swap(p[i].x, p[i].y);
                p[i].y = fa[p[i].y];
            }
            for(int i = 1; i <= k; i ++){
                for(int j = 1; j < i; j ++){
                    int lca1 = lca(p[i].x, p[j].x);
                    if(dep[lca1] >= dep[p[i].y] + 1 && dep[lca1] >= dep[p[j].y] + 1){
                        if(dep[p[i].y] > dep[p[j].y]) p[i].y = p[j].y;
                        p[j].y = lca1;
                    }
                }
            }
            for(int i = 1; i <= k; i ++){
                sum += que(1, 1, n, pos[p[i].x]);
                if(p[i].y != 0) sum -= que(1, 1, n, pos[p[i].y]);
            }
            printf("%u\n", (sum & mod));
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值