HDU-3966 Aragorn's Story【树链剖分+线段树/树状数组】\【动态树LCT】

题意

在一个 N N 个结点的树上,每个节点上都有权值。有 P P 次询问。
3种询问:

  1. I   c 1   c 2   k I\ c1\ c2\ k 表示 从c1节点到c2节点的路径上所有点权值增加k
  2. D   c 1   c 2   k D\ c1\ c2\ k 表示 从c1节点到c2节点的路径上所有点权值减少k
  3. Q   x Q\ x 表示询问x节点的权值
3 2 5
1 2 3
2 1
2 3
I 1 3 5
Q 2
D 1 2 2
Q 1 
Q 3
7
4
8






解法

1. 树链剖分

树的结构是不变的, 可以用树链剖分。

线段树

常规的树链剖分都是用线段树来实现询问更新区间和、单点询问

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define lson(x) ((x)<<1)
#define rson(x) ((x)<<1|1)
#define ms(a,v) memset(a, v, sizeof(a))
const int N = 5e4+5;

int n, m, q;
int dep[N], siz[N], fa[N], id[N], son[N], top[N];
int arr[N];
int val[N];
int num;

struct edge{
    int v, next;
}e[N*3];
int head[N];
int cnt;
void add(int u, int v)
{
    e[cnt].v = v;
    e[cnt].next = head[u];
    head[u] = cnt++;
}

void dfs1(int u, int f, int d)
{
    dep[u] = d;
    siz[u] = 1;
    son[u] = 0;
    fa[u] = f;
    for(int i=head[u];i!=-1;i=e[i].next){
        int ff = e[i].v;
        if(ff==f) continue;
        dfs1(ff, u, d+1);
        siz[u] += siz[ff];
        if(siz[son[u]] < siz[ff])
            son[u] = ff;
    }
}

void dfs2(int u, int tp)
{
    top[u] = tp;
    id[u] = ++num;
    if(son[u]) dfs2(son[u], tp);
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int ff = e[i].v;
        if(ff==fa[u] || ff==son[u]) continue;
        dfs2(ff, ff);
    }
}

int sum[N<<2], lazy[N<<2], len[N<<2];

void push_up(int x)
{
    sum[x] = sum[lson(x)] + sum[rson(x)];
}

void push_dw(int x, int l, int r)
{
    if(lazy[x])
    {
        int mid = (l+r)>>1;
        sum[lson(x)] += lazy[x] * (mid-l+1);
        sum[rson(x)] += lazy[x] * (r-mid);
        lazy[lson(x)] += lazy[x];
        lazy[rson(x)] += lazy[x];
        lazy[x] = 0;
    }
}

void build(int now, int l, int r)
{
    len[now] = r-l+1;
    lazy[now] = 0;
    if(l==r)
    {
        sum[now] = val[l];
        return;
    }
    int mid = (l+r)>>1;
    build(lson(now), l, mid);
    build(rson(now), mid+1, r);
    push_up(now);
}

int query(int now, int l, int r, int x)
{
    if(l==r)
    {
        return sum[now];
    }
    int mid = (l+r) >> 1;
    push_dw(now, l, r);
    if(x<=mid) return query(lson(now), l, mid, x);
    else return query(rson(now), mid+1, r, x);
}

void update(int x, int L, int R, int l, int r, int v)
{
    if(L==l && R==r){
        sum[x] += v * (R-L+1);
        lazy[x] += v;
        return;
    }
    push_dw(x, L, R);
    int mid = (L+R) >> 1;
    if(r<=mid) update(lson(x), L, mid, l, r, v);
    else if(l>mid) update(rson(x), mid+1, R, l, r, v);
    else{
        update(lson(x), L, mid, l, mid, v);
        update(rson(x), mid+1, R, mid+1, r, v);
    }
    push_up(x);
}

void TreUpdate(int u, int v, int vv)
{
    int tp1 = top[u], tp2 = top[v];
    while(tp1!=tp2)
    {
        if(dep[tp1] < dep[tp2]){
            swap(tp1, tp2);
            swap(u, v);
        }
        update(1, 1, n, id[tp1], id[u], vv);
        u = fa[tp1];
        tp1 = top[u];
    }
    
    if(dep[u] > dep[v]) swap(u,v);
    update(1, 1, n, id[u], id[v], vv);
}

int main() {
    
    while(~scanf("%d%d%d", &n, &m, &q))
    {
        ms(head, -1);
        cnt = 0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d", &arr[i]);
        }
        int x, y;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d", &x, &y);
            add(x, y);
            add(y, x);
        }
        num = 0;
        dfs1(1, 0, 1);
        dfs2(1, 1);
        for(int i=1;i<=n;i++)
            val[id[i]] = arr[i];
        build(1, 1, n);
        int a, b, vv; getchar();
        for(int i=0;i<q;i++)
        {
            char op[3];
            cin >> op;
            if(op[0]=='I')
            {
                scanf("%d%d%d", &a, &b, &vv);
                TreUpdate(a, b, vv);
            }
            else if(op[0]=='D')
            {
                scanf("%d%d%d", &a, &b, &vv);
                TreUpdate(a, b, -vv);
            }
            else if(op[0]=='Q')
            {
                scanf("%d", &a);
                printf("%d\n",query(1, 1, n, id[a]));
            }
            getchar();
        }
    }
    return 0;
}

树状数组

树状数组也可以实现区间更新、单点询问
比如要在区间[u, v]上加上1,就只要add(u, 1), add(v+1, -1)
询问x的值,就是getsum(x)(即前缀和)

/*
树链剖分部分略
*/
int lowbit(int x){ return x&(-x); }
int c[N], maxx;
void add2(int i, int v)
{
    while(i<=n){
        c[i] += v;
        i += lowbit(i);
    }
}

int sum(int i)
{
    int s = 0;
    while(i>0)
    {
        s += c[i];
        i -= lowbit(i);
    }
    return s;
}


void TreUpdate(int u, int v, int vv)
{
    int tp1 = top[u], tp2 = top[v];
    while(tp1!=tp2)
    {
        if(dep[tp1] < dep[tp2]){
            swap(tp1, tp2);
            swap(u, v);
        }
        add2(id[tp1], vv);
        add2(id[u]+1, -vv);
        u = fa[tp1];
        tp1 = top[u];
    }
    
    if(dep[u] > dep[v]) swap(u,v);
    add2(id[u], vv);
    add2(id[v]+1, -vv);
}

int main() {
    
    while(~scanf("%d%d%d", &n, &m, &q))
    {
        ms(head, -1);
        ms(c, 0);
        cnt = 0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d", &arr[i]);
        }
        int x, y;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d", &x, &y);
            add(x, y);
            add(y, x);
        }
        num = 0;
        dfs1(1, 0, 1);
        dfs2(1, 1);
        for(int i=1;i<=n;i++){
            add2(id[i], arr[i]);
            add2(id[i]+1, -arr[i]);
        }
        int a, b, vv; getchar();
        for(int i=0;i<q;i++)
        {
            char op[3];
            cin >> op;
            if(op[0]=='I')
            {
                scanf("%d%d%d", &a, &b, &vv);
                TreUpdate(a, b, vv);
            }
            else if(op[0]=='D')
            {
                scanf("%d%d%d", &a, &b, &vv);
                TreUpdate(a, b, -vv);
            }
            else if(op[0]=='Q')
            {
                scanf("%d", &a);
                printf("%d\n",sum(id[a]));
            }
            getchar();
        }
    }
    return 0;
}

2. 动态树

在lct的splay树上加上lazy标记,每次更新用split分离c1,c2,在c2懒标记上加上v,待之后下传。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;

int fa[maxn], ch[maxn][2], val[maxn];
bool tag[maxn];
int lazy[maxn];

int n, m, q, a[maxn];

#define ls(x) (ch[x][0])
#define rs(x) (ch[x][1])
#define fa(x) (fa[x])
inline bool ident(int x, int f) { return rs(f)==x; }
inline void connect(int x, int f,int s) { ch[f][s] = x; fa(x) = f; }
//#define update(x) spl[x].res=spl[ls(x)].res^spl[rs(x)].res^spl[x].val
inline bool nroot(int x) {return ls(fa(x))==x||rs(fa(x))==x; }
inline void rever(int x) {std::swap(ls(x), rs(x)); tag[x]^=1;}

inline int read(){
    int x=0,w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return w?-x:x;
}

inline void update(int x)
{
    
}

inline void pushdw(int x)
{
    if(tag[x])
    {
        if(ls(x)) rever(ls(x));
        if(rs(x)) rever(rs(x));
    }
    tag[x] = 0;
    if(lazy[x])
    {
        if(ls(x)) {val[ls(x)] += lazy[x]; lazy[ls(x)] += lazy[x];}
        if(rs(x)) {val[rs(x)] += lazy[x]; lazy[rs(x)] += lazy[x];}
    }
    lazy[x] = 0;
}
void pushall(int x)
{
    if(nroot(x)) pushall(fa(x));
    pushdw(x);
}
inline void rotate(int x)
{
    int f = fa(x), ff=fa(f), k=ident(x, f);
    connect(ch[x][k^1], f, k);
    fa(x)=ff;
    if(nroot(f)) ch[ff][ident(f, ff)]=x;
    connect(f, x, k^1);
    update(f); update(x);
}
inline void splaying(int x)
{
    pushall(x);
    while(nroot(x))
    {
        int f=fa(x), ff=fa(f);
        if(nroot(f)) ident(f, ff)^ident(x,f)?rotate(x):rotate(f);
        rotate(x);
    }
}

inline void access(int x)
{
    for(int y=0;x;x=fa(x)){
        splaying(x);
        rs(x) = y;
        update(x);
        y = x;
    }
}

inline void mkroot(int x)
{
    access(x);
    splaying(x);
    rever(x);
}

inline int findroot(int x)
{
    access(x);
    splaying(x);
    while(ls(x))
    {
        pushdw(x);
        x = ls(x);
    }
    splaying(x);
    return x;
}

inline void link(int x, int y)
{
    mkroot(x);
    if(findroot(y)!=x)
        fa(x) = y;
}

inline void cut(int x, int y)
{
    mkroot(x);
    //如果x和y不在一棵树上,or x和y不是紧紧挨着的,就return
    if(findroot(y)!=x || fa(y)!=x || ls(y)) return;
    fa(y) = rs(x) = 0;
    update(x);
}

inline void split(int x, int y)
{
    mkroot(x);
    access(y);
    splaying(y);
    //此时y包含整条链的信息,只需访问y即可
}

int main() {
    while(~scanf("%d%d%d",&n, &m, &q)){
        memset(lazy, 0, sizeof lazy);
        memset(val, 0, sizeof val);
        memset(tag, 0, sizeof tag);
        memset(fa, 0, sizeof fa);
        memset(ch, 0, sizeof ch);
        for (int i = 1; i <= n; i++) {
            val[i] = read();
        }
        int x, y;
        for(int i=0;i<m;i++)
        {
            x = read(); y = read();
            link(x, y);
        }
        char op[3];
        while(q--)
        {
            scanf("%s", op);
            if(op[0]=='Q'){
                x = read();
                access(x);
                printf("%d\n", val[x]);
            }
            else{
                x = read(); y = read();
                int v = read();
                if(op[0]=='D') v = -v;
                split(x, y);
                val[y] += v;
                lazy[y] += v;
            }
        }
    }
    return 0;
}
发布了40 篇原创文章 · 获赞 15 · 访问量 1808

猜你喜欢

转载自blog.csdn.net/irimsky/article/details/104841490