树链剖分

https://www.cnblogs.com/chinhhh/p/7965433.html#dfs1

 

 

 树链剖分跳链logn的复杂度。

 

这个是权值在点上,先跳到一条重链上同时查询途中经过的点,如果在一条重链上就直接查询。

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

const int N =200000+10;
vector<int> g[N];
int w[N],wt[N];
//w[]初始的、wt[]新的点权数组
int a[N<<2],laz[N<<2];
//线段树数组、lazy操作
int son[N],id[N],fa[N],cnt,dep[N],siz[N],top[N];
//son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点
int res;
//查询答案
int n,m,r,mod;
//-------------------------------------- 以下为线段树
inline void pushdown(int rt,int len){
    if(laz[rt]) {
        laz[rt<<1]+=laz[rt];
        laz[rt<<1|1]+=laz[rt];
        a[rt<<1]+=laz[rt]*(len-(len>>1));
        a[rt<<1|1]+=laz[rt]*(len>>1);
        a[rt<<1]%=mod;
        a[rt<<1|1]%=mod;
        laz[rt]=0;
    }
}

void pushup(int rt) {
    a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;
}

void build(int rt,int l,int r){
   laz[rt] = 0;
if(l==r){ a[rt]=wt[l]; if(a[rt]>mod)a[rt]%=mod; return; } int m = (l + r) >> 1; build(rt << 1, l, m); build(rt << 1 | 1, m + 1, r); pushup(rt); } void query(int rt,int l,int r,int le,int re){ if(le<=l&&r<=re){ res+=a[rt]; res%=mod; return; } pushdown(rt,r - l + 1); int m = (l + r) >> 1; if(re <= m) query(rt << 1, l, m, le, re); else if(le > m) query(rt << 1 | 1, m + 1, r, le, re); else { query(rt << 1, l, m, le, m); query(rt << 1 | 1, m + 1, r, m + 1, re); } } void update(int rt,int l,int r,int le,int re,int k){ if(le<=l&&re >= r){ laz[rt]+=k; a[rt]+=k*(r - l + 1); if(a[rt] > mod) a[rt] %= mod; return; } pushdown(rt,r - l + 1); int m = (l + r) >> 1; if(re <= m) update(rt << 1, l, m, le, re, k); else if(le > m) update(rt << 1 | 1, m + 1, r, le, re, k); else { update(rt << 1, l, m, le, m, k); update(rt << 1 | 1, m + 1, r, m + 1, re, k); } pushup(rt); } //---------------------------------以上为线段树 int qRange(int x,int y){ int ans=0; while(top[x]!=top[y]){//当两个点不在同一条链上 if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点 res=0; query(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和 ans+=res; ans%=mod;//按题意取模 x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点 } //直到两个点处于一条链上 if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点 res=0; query(1,1,n,id[x],id[y]);//这时再加上此时两个点的区间和即可 ans+=res; return ans%mod; } void updRange(int x,int y,int k){//同上 k%=mod; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); update(1,1,n,id[top[x]],id[x],k); x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); update(1,1,n,id[x],id[y],k); } //查询他自己以及他儿子节点的权值总和。 int qSon(int x){ res=0; query(1,1,n,id[x],id[x]+siz[x]-1);//子树区间右端点为id[x]+siz[x]-1 return res; } //更新一个点自己以及他的儿子节点。 void updSon(int x,int k){//同上 k %= mod; update(1,1,n,id[x],id[x]+siz[x]-1,k); } void dfs1(int x,int f,int deep){//x当前节点,f父亲,deep深度 dep[x]=deep;//标记每个点的深度 fa[x]=f;//标记每个点的父亲 siz[x]=1;//标记每个非叶子节点的子树大小 int maxson=-1;//记录重儿子的儿子数 int len = g[x].size(); for(int i = 0; i < len; i++){ int y=g[x][i]; if(y==f)continue;//若为父亲则continue dfs1(y,x,deep+1);//dfs其儿子 siz[x]+=siz[y];//把它的儿子数加到它身上 if(siz[y]>maxson)son[x]=y,maxson=siz[y];//标记每个非叶子节点的重儿子编号 } } void dfs2(int x,int topf){//x当前节点,topf当前链的最顶端的节点 id[x]=++cnt;//标记每个点的新编号 wt[cnt]=w[x];//把每个点的初始值赋到新编号上来 top[x]=topf;//这个点所在链的顶端 if(!son[x])return;//如果没有儿子则返回 dfs2(son[x],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理 int len = g[x].size(); for(int i = 0; i < len; i++){ int y=g[x][i]; if(y==fa[x]||y==son[x])continue; dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链 } } int main(){ scanf("%d%d%d%d", &n, &m, &r, &mod); for(int i=1;i<=n;i++) scanf("%d", &w[i]); int u, v;
   for(int i = 1; i <= n; i++) g[i].clear();
   memset(son, 0, sizeof(son));
for(int i=1;i<n;i++){ scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } cnt = 0; dfs1(r,0,1); dfs2(r,r); build(1,1,n); while(m--){ int k,x,y,z; scanf("%d", &k); if(k==1){ scanf("%d%d%d", &x, &y, &z); updRange(x,y,z); } else if(k==2){ scanf("%d%d", &x, &y); printf("%d\n",qRange(x,y)); } else if(k==3){ scanf("%d%d", &x, &y); updSon(x,y); } else{ scanf("%d", &x); printf("%d\n",qSon(x)); } } }

 

 

 

https://cn.vjudge.net/contest/28982#problem/C

 

这个权值是在边上,每个边的权值算在这条边较深的那个点上,在跳重链的时候和权值在点上是一样的(结合图想一下,重链之间没有重复的点,所以跳到另一条链时直接可以是从top节点开始)

但是两个点在一条重链上时,就应该和权值在点上有所不同,因为只能算边,所以最上面那个点不能算。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 10000+10;
const int INF = 0x3f3f3f3f;

struct Node {
    int v, w;
    Node(int v = 0, int w = 0) : v(v),w(w) {};
} node[N];
vector<Node> g[N];
int w[N],wt[N];
pii p[N];
//w[]初始的、wt[]新的点权数组
int laz[N<<2], ma[N << 2], mi[N << 2];
//线段树数组、lazy操作
int son[N],id[N],fa[N],cnt,dep[N],siz[N],top[N];
//son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点
int res;
//查询答案
int n,m,r,mod;
//-------------------------------------- 以下为线段树
inline void pushdown(int rt,int len){
    if(laz[rt] == -1) {
        laz[rt<<1]*=laz[rt];
        laz[rt<<1|1]*=laz[rt];
        ma[rt << 1] *= laz[rt];
        mi[rt << 1] *= laz[rt];
        swap(ma[rt << 1], mi[rt << 1]);
        ma[rt << 1 | 1] *= laz[rt];
        mi[rt << 1 | 1] *= laz[rt];
        swap(ma[rt << 1 | 1], mi[rt << 1 | 1]);
        laz[rt]=1;
    }
}

void pushup(int rt) {
    ma[rt] = max(ma[rt << 1], ma[rt << 1 | 1]);
    mi[rt] = min(mi[rt << 1], mi[rt << 1 | 1]);
}

void build(int rt,int l,int r){
    laz[rt] = 1;
    if(l==r){
        mi[rt]=wt[l];
        ma[rt] = wt[l];
        return;
    }
    int m = (l + r) >> 1;
    build(rt << 1, l, m);
    build(rt << 1 | 1, m + 1, r);
    pushup(rt);
}

void querymax(int rt, int l, int r, int le, int re) {
    if(le <= l && re >= r) {
        res = max(res, ma[rt]);
        return;
    }
    pushdown(rt, r - l + 1);
    int m = (l + r) >> 1;
    if(re <= m) querymax(rt << 1, l, m, le, re);
    else if(le > m) querymax(rt << 1 | 1, m + 1, r, le, re);
    else {
        querymax(rt << 1, l, m, le, m);
        querymax(rt << 1 | 1, m + 1, r, m + 1, re);
    }
}

void update(int rt,int l,int r,int le,int re){
    if(le<=l&&re >= r){
        laz[rt] = laz[rt] * (-1);
        ma[rt] = ma[rt] * (-1);
        mi[rt] = mi[rt] * (-1);
        swap(ma[rt], mi[rt]);
        return;
    }
    pushdown(rt,r - l + 1);
    int m = (l + r) >> 1;
    if(re <= m) update(rt << 1, l, m, le, re);
    else if(le > m) update(rt << 1 | 1, m + 1, r, le, re);
    else {
        update(rt << 1, l, m, le, m);
        update(rt << 1 | 1, m + 1, r, m + 1, re);
    }
    pushup(rt);
}


void updatepos(int rt, int l, int r, int pos, int k) {
    if(l == r) {
        ma[rt] = mi[rt] = k;
        return;
    }
    pushdown(rt, r - l + 1);
    int m = (l + r) >> 1;
    if(pos <= m) updatepos(rt << 1, l, m, pos, k);
    else updatepos(rt << 1 | 1, m + 1, r, pos, k);
    pushup(rt);
}

//---------------------------------以上为线段树
int qRange(int x,int y) {
    int ans=-INF;
    while(top[x]!=top[y]){//当两个点不在同一条链上
        if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点
        res=-INF;
        querymax(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和
        ans=max(ans, res);
        x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
    }
    //直到两个点处于一条链上
    if(x==y) return ans;
    if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点
    res=-INF;
    querymax(1,1,n,id[son[x] ],id[y]);//这时再加上此时两个点的区间和即可
    ans=max(ans, res);
    return ans;
}

void updRange(int x,int y){//同上
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        update(1,1,n,id[top[x]],id[x]);
        x=fa[top[x]];
    }
    if(x == y) return;
    if(dep[x]>dep[y])swap(x,y);
    update(1,1,n,id[son[x] ],id[y]);
}

void dfs1(int x,int f,int deep){//x当前节点,f父亲,deep深度
    dep[x]=deep;//标记每个点的深度
    fa[x]=f;//标记每个点的父亲
    siz[x]=1;//标记每个非叶子节点的子树大小
    int maxson=-1;//记录重儿子的儿子数
    int len = g[x].size();
    for(int i=0;i<len;i++){
        int y=g[x][i].v;
        int ww=g[x][i].w;
        if(y==f)continue;//若为父亲则continue
        dfs1(y,x,deep+1);//dfs其儿子
        w[y]=ww;
        siz[x]+=siz[y];//把它的儿子数加到它身上
        if(siz[y]>maxson)son[x]=y,maxson=siz[y];//标记每个非叶子节点的重儿子编号
    }
}

void dfs2(int x,int topf){//x当前节点,topf当前链的最顶端的节点
    id[x]=++cnt;//标记每个点的新编号
    wt[cnt]=w[x];//把每个点的初始值赋到新编号上来
    top[x]=topf;//这个点所在链的顶端
    if(!son[x])return;//如果没有儿子则返回
    dfs2(son[x],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理
    int len = g[x].size();
    for(int i = 0; i < len; i++){
        int y=g[x][i].v;
        if(y==fa[x]||y==son[x])continue;
        dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链
    }
}

int t;
int main(){
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        int u, v, w;
        for(int i = 1; i <= n; i++) g[i].clear();
        memset(son, 0, sizeof(son));
        for(int i=1;i<n;i++){
            scanf("%d%d%d", &u, &v, &w);
            p[i] = pii(u, v);
            g[u].push_back(Node(v, w));
            g[v].push_back(Node(u, w));
        }
        cnt = 0;
        dfs1(1,0,1);
        dfs2(1,1);
        build(1,1,n);
        char ch[10];
        while(~scanf("%s", ch)){
            int x, y;
            if(ch[0] == 'Q') {
                scanf("%d%d", &x, &y);
                printf("%d\n",qRange(x, y));
            }
            else if(ch[0] == 'C') {
                scanf("%d%d", &x, &y);
                if(dep[p[x].first] < dep[p[x].second]) {
                    swap(p[x].first, p[x].second);
                }
                updatepos(1, 1, n, id[p[x].first ], y);
            }
            else if(ch[0] == 'N'){
                scanf("%d%d", &x, &y);
                updRange(x, y);
            }
            else break;
        }
    }
}

 

posted @ 2019-08-18 20:51  downrainsun  阅读(177)  评论(0编辑  收藏  举报