2018 有志气博士来种草

Task
N节点的树,m个操作分2种:
① (x,y)路径上的各边的值+1
② 询问边(x,y)的值。

Solution
方法一:树链剖分
树链剖分使用于树上的区间更新或者区间询问,把链重新编号,加入线段树中。

方法二:刷漆法
问题属于区间更新,单点求值。
如果在序列上,可以用刷漆法+前缀和得到每个点的权值。
在树上可以转化成两段区间,即(x,lca),(y,lca)同样的左端点+1,右端点+1的位置-1
结论:x到父亲的边权值=x子树的权值和

但是暴力dfs x的子树会超时,如果把x的子树变成连续的一段,就可以用线段树进行区间求值。而dfs序可以将一棵子树变成连续的一段。

int L[M],R[M],fa[S][M],head[M],dep[M];
int n,m,ecnt,tot;
struct edge{
    int t,nxt;
}e[M<<1];
struct Segment_Tree{
    int t[M<<2];//存当前点的值 
    inline void update(int l,int r,int x,int a,int p){
        if(l==r){
            t[p]+=a;
            return;
        }   
        int mid=l+r>>1;
        if(x<=mid)update(l,mid,x,a,lsn(p));
        else update(mid+1,r,x,a,rsn(p));
        t[p]=t[lsn(p)]+t[rsn(p)];
    }
    inline int query(int L,int R,int l,int r,int p){
        if(l==L&&r==R)return t[p];
        int mid=L+R>>1;
        if(r<=mid)return query(L,mid,l,r,lsn(p));
        else if(l>mid)return query(mid+1,R,l,r,rsn(p));
        else return query(L,mid,l,mid,lsn(p))+query(mid+1,R,mid+1,r,rsn(p));
    }
}T;
struct P100{
    inline void addedge(int f,int t){
        e[++ecnt]=(edge){t,head[f]};
        head[f]=ecnt;
    }
    inline void input(){
        int a,b;
        rd(n);rd(m);
        rep(i,1,n-1){
            rd(a);rd(b);
            addedge(a,b);
            addedge(b,a);
        }
    }
    inline void dfs(int f,int x,int d){
        fa[0][x]=f;
        dep[x]=d;
        L[x]=++tot;
        tral(i,x){
            if(e[i].t!=f)dfs(x,e[i].t,d+1);
        }
        R[x]=tot;
    }
    inline int LCA(int x,int y){
        if(dep[x]<dep[y])swap(x,y);
        int d=dep[x]-dep[y];
        rep(i,0,S-1){
            if(d&(1<<i))x=fa[i][x];
        }
        if(x==y)return x;
        per(i,S-1,0){
            if(fa[i][x]!=fa[i][y]){
                x=fa[i][x];
                y=fa[i][y];
            }
        }
        return fa[0][x];
    }
    inline void solve(){
        int a,b;
        char str[10];
        rep(i,1,m){
            scanf("%s",str);
            rd(a);rd(b);
            if(str[0]=='P'){//add
                T.update(1,n,L[a],1,1);
                T.update(1,n,L[b],1,1);
                T.update(1,n,L[LCA(a,b)],-2,1);
            }
            else{
                if(dep[a]<dep[b])swap(a,b);
                sc(T.query(1,n,L[a],R[a],1));//子树的和 
            }
        }
    }
    inline void init(){
        rep(i,1,S-1)
            rep(j,1,n)fa[i][j]=fa[i-1][fa[i-1][j]];
    }
    inline void MAIN(){
        input();    
        dfs(0,1,0);
        init();
        solve();
    }
}P100;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值