HDU 3966 /// 树链剖分+树状数组

题意:

http://acm.hdu.edu.cn/showproblem.php?pid=3966

给一棵树,并给定各个点权的值,然后有3种操作:

I x y z : 把x到y的路径上的所有点权值加上z

D x y z:把x到y的路径上的所有点权值减去z

Q z:查询节点编号为x的权值

 

这里主要放下用树状数组维护的模板

区间修改单点查询 好像用线段树更好?

em.... 两种都放好了~ 

好像说hduoj是windows系统容易爆栈 手动扩栈加这句 

#pragma comment(linker, "/STACK:1024000000,1024000000")

 

#include <bits/stdc++.h>
using namespace std;
#define mem(i,j) memset(i,j,sizeof(i))
#define LL long long
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

const int maxn = 5e4 + 10;
const int maxnode = maxn<<2;

LL head[maxn], tot, pos;
LL fa[maxn], son[maxn], dep[maxn], num[maxn];
// i的父亲、i的重结点、i的深度、i的儿子个数
LL top[maxn], p[maxn], fp[maxn];
// i所在链的顶端、ID->dfsID、dfsID->ID
LL n,m,q;
LL val[maxn];
struct Edge { int to,ne; }e[maxnode];
void init() {
    tot=1; pos=0;
    mem(head,0);  mem(son,0);
}
void add(int u,int v) {
    e[tot].to = v;
    e[tot].ne = head[u];
    head[u] = tot++;
}

struct IntervalTree {
    LL _sum, _min, _max;
    LL sumv[maxnode], minv[maxnode], maxv[maxnode], setv[maxnode], addv[maxnode];
    void init() {
        mem(sumv,0); mem(setv,0); mem(addv,0);
    }

    void maintain(int L, int R, int rt) {
        int lc = rt<<1, rc = rt<<1|1;
        if(R > L) {
            sumv[rt] = sumv[lc] + sumv[rc];
            minv[rt] = min(minv[lc], minv[rc]);
            maxv[rt] = max(maxv[lc], maxv[rc]);
        }
        if(setv[rt] >= 0) {
            minv[rt] = maxv[rt] = setv[rt];
            sumv[rt] = setv[rt] * (R-L+1);
        }
        if(addv[rt]) {
            minv[rt] += addv[rt]; maxv[rt] += addv[rt];
            sumv[rt] =sumv[rt]+addv[rt] * (R-L+1);
        }
    }

    void pushdown(int rt) {
        int lc = rt*2, rc = rt*2+1;
        if(setv[rt] >= 0) {
            setv[lc] = setv[rc] = setv[rt];
            addv[lc] = addv[rc] = 0; setv[rt] = -1;
        }
        if(addv[rt]) {
            addv[lc] += addv[rt]; addv[rc] += addv[rt];
            addv[rt] = 0;
        }
    }

    ///update(更新区间左右端点、更新值、更新选项 op=1为加减 op!=1为置值、当前区间左右端点、根)
    void update(int L, int R, LL v, int op, int l, int r, int rt){
        //int lc = rt<<1, rc = rt<<1|1;
        if(L <= l && R >= r) {
            if(op == 1) addv[rt] += v;
            else { setv[rt] = v; addv[rt] = 0; }
        } else {
            pushdown(rt);
            int m = l + (r-l)/2;
            if(L <= m) update(L, R, v, op, lson);
            else maintain(lson);
            if(R > m) update(L, R, v, op, rson);
            else maintain(rson);
        }
        maintain(l, r, rt);
    }

    ///query(问询的左右端点、累加lazy_tag的累加量、当前区间左右端点、根)
    void query(int L, int R, LL add, int l, int r, int rt) {
        if(setv[rt] >= 0) {
            LL v = setv[rt] + add + addv[rt];
            _sum += v * (LL)(min(r,R)-max(l,L)+1);
            _min = min(_min, v);
            _max = max(_max, v);
        } else if(L <= l && R >= r) {
            _sum += sumv[rt] + add * (LL)(r-l+1);
            _min = min(_min, minv[rt] + add);
            _max = max(_max, maxv[rt] + add);
        } else {
            int m = l + (r-l)/2;
            if(L <= m) query(L, R, add+addv[rt], lson);
            if(R > m) query(L, R, add+addv[rt], rson);
        }
    }
}T;

/** -----树链剖分----- */

void dfs1(int u,int pre,int d) {
    dep[u]=d;  fa[u]=pre;  num[u]=1;
    for(int i=head[u];i;i=e[i].ne) {
        int v=e[i].to;
        if(v!=fa[u]) {
            dfs1(v,u,d+1);  num[u]+=num[v];
            if(!son[u] || num[v]>num[son[u]]) son[u]=v;
        }
    }
}
void dfs2(int u,int sp) {
    top[u]=sp;  p[u]=++pos;  fp[p[u]]=u;
    if(!son[u]) return;  dfs2(son[u],sp);
    for(int i=head[u];i;i=e[i].ne) {
        int v=e[i].to;
        if(v!=son[u] && v!=fa[u]) dfs2(v,v);
    }
}
// 查询树上x到y的总和
LL queryPath(int x,int y) {
    LL ans=0LL;
    int fx=top[x], fy=top[y];
    // fx==fy 说明到了LCA
    while(fx!=fy) { // x y不在同一条重链上
        if(dep[fx]>=dep[fy]) {
            T._sum=0LL; T.query(p[fx],p[x],0,1,pos,1);
            ans=(ans+T._sum)%mod;
            x=fa[fx];
        } else {
            T._sum=0LL; T.query(p[fy],p[y],0,1,pos,1);
            ans=(ans+T._sum)%mod;
            y=fa[fy];
        } // 先加离LCA更远的 且只加到父亲节点的一段 一步步移
        fx=top[x], fy=top[y];
    } // 直到两点在同一条重链上跳出 此时节点必连续

    // 将最后到达LCA的一段连续的区间加上
    if(p[x]>p[y]) swap(x,y);
    T._sum=0LL; T.query(p[x],p[y],0,1,n,1);

    return (ans+T._sum)%mod;
}
// 将树上x到y都加上z (和queryPath()差不多)
void updatePath(LL x,LL y,LL z) {
    int fx=top[x], fy=top[y];
    while(fx!=fy) {
        if(dep[fx]>=dep[fy]) {
            T.update(p[fx],p[x],z,1,1,n,1);
            x=fa[fx];
        } else {
            T.update(p[fy],p[y],z,1,1,n,1);
            y=fa[fy];
        }
        fx=top[x], fy=top[y];
    }

    if(p[x]>p[y]) swap(x,y);
    T.update(p[x],p[y],z,1,1,n,1);
}

/** ---------------- */

int main()
{
    while(~scanf("%lld%lld%lld",&n,&m,&q)) {
        init();
        for(int i=1;i<=n;i++)
            scanf("%lld",&val[i]);
        for(int i=0;i<m;i++) {
            int a,b; scanf("%d%d",&a,&b);
            add(a,b); add(b,a);
        }
        dfs1(1,0,1); // 根节点 前驱节点 深度
        dfs2(1,1); // 当前节点 起始重结点
        T.init();
        for(int i=1;i<=n;i++)
            T.update(p[i],p[i],val[fp[p[i]]],1,1,n,1);

        while(q--) {
            LL x,y,z; char op;
            scanf(" %c",&op); //printf("op%d\n",op);
            if(op=='Q') {
                scanf("%lld",&x);
                T._sum=0; T.query(p[x],p[x],0LL,1,n,1);
                printf("%lld\n",T._sum);
            } else {
                scanf("%lld%lld%lld",&x,&y,&z);
                if(op=='D') z=-z;
                updatePath(x,y,z);
            }
        }
    }

    return 0;
}

线段树
线段树
#include <bits/stdc++.h>
using namespace std;
#define mem(i,j) memset(i,j,sizeof(i))
#define LL long long
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

const int maxn = 5e4 + 10;
const int maxnode = maxn<<2;
const int maxedge = maxn<<2;

LL head[maxn], tot, pos;
LL fa[maxn], son[maxn], dep[maxn], num[maxn];
// i的父亲、i的重结点、i的深度、i的儿子个数
LL top[maxn], p[maxn], fp[maxn];
// i所在链的顶端、ID->dfsID、dfsID->ID
LL n,m,q;
LL val[maxn];
struct Edge { int to,ne; }e[maxedge];
void init() {
    tot=1; mem(head,0);
    pos=0; mem(son,0);
}
void add(int u,int v) {
    e[tot].to = v;
    e[tot].ne = head[u];
    head[u] = tot++;
}

struct Tree {
    int N;
    LL sumT[maxn];
    void init() {
        N=1;
        while(N<=n) N<<=1;
        mem(sumT,0);
    }
    int lowbit(int i) { return -i&i; }
    void add(int i,LL x) {
        while(i<=N) {
            sumT[i]+=x;
            i+=lowbit(i);
        }
    }
    LL sum(int i) {
        LL res=0LL;
        while(i) {
            res+=sumT[i];
            i-=lowbit(i);
        } return res;
    }
}T;

/** -----树链剖分----- */

void dfs1(int u,int pre,int d) {
    dep[u]=d;  fa[u]=pre;  num[u]=1;
    for(int i=head[u];i;i=e[i].ne) {
        int v=e[i].to;
        if(v!=fa[u]) {
            dfs1(v,u,d+1);  num[u]+=num[v];
            if(!son[u] || num[v]>num[son[u]]) son[u]=v;
        }
    }
}
void dfs2(int u,int sp) {
    top[u]=sp;  p[u]=++pos;  fp[p[u]]=u;
    if(!son[u]) return;  dfs2(son[u],sp);
    for(int i=head[u];i;i=e[i].ne) {
        int v=e[i].to;
        if(v!=son[u] && v!=fa[u]) dfs2(v,v);
    }
}
// 将树上x到y都加上z (和queryPath()差不多)
void updatePath(LL x,LL y,LL z) {
    int fx=top[x], fy=top[y];
    while(fx!=fy) {
        if(dep[fx]>=dep[fy]) {
            T.add(p[fx],z); T.add(p[x]+1,-z);
            x=fa[fx];
        } else {
            T.add(p[fy],z); T.add(p[y]+1,-z);
            y=fa[fy];
        }
        fx=top[x], fy=top[y];
    }

    if(p[x]>p[y]) swap(x,y);
    T.add(p[x],z); T.add(p[y]+1,-z);
}

/** ---------------- */

int main()
{
    while(~scanf("%lld%lld%lld",&n,&m,&q)) {
        init();
        for(int i=1;i<=n;i++)
            scanf("%lld",&val[i]);
        for(int i=0;i<m;i++) {
            int a,b; scanf("%d%d",&a,&b);
            add(a,b); add(b,a);
        }
        dfs1(1,0,1); // 根节点 前驱节点 深度
        dfs2(1,1); // 当前节点 起始重结点
        T.init();
        for(int i=1;i<=n;i++)
            T.add(p[i],val[i]),
            T.add(p[i]+1,-val[i]);

        while(q--) {
            LL x,y,z; char op;
            scanf(" %c",&op);
            if(op=='Q') {
                scanf("%lld",&x);
                printf("%lld\n",T.sum(p[x]));
            } else {
                scanf("%lld%lld%lld",&x,&y,&z);
                if(op=='D') z=-z;
                updatePath(x,y,z);
            }
        }
    }

    return 0;
}
树状数组
posted @ 2018-11-20 17:39  _Jessie  阅读(284)  评论(1编辑  收藏  举报