zoukankan      html  css  js  c++  java
  • 树链剖分

    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 ",qRange(x,y)); } else if(k==3){ scanf("%d%d", &x, &y); updSon(x,y); } else{ scanf("%d", &x); printf("%d ",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
    ",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;
            }
        }
    }
  • 相关阅读:
    ajax 传递参数中文乱码解决办法
    jQuery 时间戳转化成时间
    IDEA2017 导入 SVN上的 Myeclipse或Eclipse 项目
    ajax返回json数据,对其中日期的解析
    MYSQL 按照字母排序查询
    JVM介绍
    正则表达式
    could not find the main class错误
    转:MyEclipse使用总结——MyEclipse10安装SVN插件
    转:Oracle数据库sqlplus与plsqldev解决乱码
  • 原文地址:https://www.cnblogs.com/downrainsun/p/11373882.html
Copyright © 2011-2022 走看看