GDKOI2015 Day1第四题 树

题目

这里写图片描述

题解

题目条件

①点分了颜色,点的颜色可以随便变。
②询问的是路径。
③子树内点的颜色可以交换。(修改子树)(颜色≤10)

解决方案

很容易想到的一种方法,那就是树链剖分。但是复杂度带2个log,可能会超时。
对于条件③,可以开10棵线段树,同棵线段树中维护着相同颜色的点的权值。
修改子树,询问路径,那么就用括号序来解决问题。
括号序,通俗的讲,就是遍历一遍树走到的点的顺序。那么显然一个点进一次出一次。设 L[x] 表示第一次遍历x时出现的位置。 R[x] 表示最后一次。
整个序列的长度为 2N
那么怎么查询路径的点权和?设 sum[i] 表示前缀和,在 L[x] 上加上 weight[x] ,在 R[x] 上减去 weight[x] 。那么 sum[L[x]] 就是根节点到x的权值和。不在这条链上的权值被抵消掉了。
查询一条路径,就是 sum[L[x]]+sum[L[y]]sum[L[lca(x,y)]]
那么对子树内的点颜色交换,可以交换两棵线段树中 L[x] R[x] 之间的区间。

心得

括号序不仅可以维护子树的权值和,也可以维护路径的权值和。
但是这种做法不能够支持链上修改,也不能维护最值。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100010
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
    int ls,rs,sum;
};note tr[N*50];
int c[N],v[N];
struct noe{
    int to,next;
};noe edge[N];
int head[N],tot,gs,cnt,root[N];
int L[N],R[N],fa[N][20],dep[N],tar[N];
int i,j,k,n,m,x,y,z,ans,lca;
char ch;
int read(){
    int rs=0,fh=1;char ch;
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')fh=-1,ch=getchar();
    while(ch>='0'&&ch<='9')rs=rs*10+ch-'0',ch=getchar();
    return rs*fh;
}
int write(int x){if(x>9)write(x/10);P(x%10+'0');}
int Write(int x){if(x<0)P('-'),write(x);else write(x);}
void lb(int x,int y){edge[++tot].to=y;edge[tot].next=head[x];head[x]=tot;}
void updt(int ps){
    tr[ps].sum=tr[tr[ps].ls].sum+tr[tr[ps].rs].sum;
}
void dg(int x){
    L[x]=++gs;tar[gs]=x;
    for(int i=head[x];i;i=edge[i].next)
        if(edge[i].to!=fa[x][0]){
            fa[edge[i].to][0]=x;
            dep[edge[i].to]=dep[x]+1;
            dg(edge[i].to);
        }
    R[x]=++gs;tar[gs]=x;
}
int getlca(int x,int y){
    int i;
    if(dep[x]>dep[y])swap(x,y);
    fd(i,19,0)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
    if(x==y)return x;
    fd(i,19,0)if(dep[fa[x][i]]!=dep[fa[y][i]])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int query(int ps,int l,int r,int x,int y){
    if(!ps)return 0;
    if(l==x && r==y)return tr[ps].sum;
    int mid=(l+r)>>1;
    if(y<=mid)return query(tr[ps].ls,l,mid,x,y);else
    if(x>mid)return query(tr[ps].rs,mid+1,r,x,y);else
    return query(tr[ps].ls,l,mid,x,mid)+query(tr[ps].rs,mid+1,r,mid+1,y);
}
void change(int &ps,int l,int r,int x,int z){
    if(!ps)ps=++gs;
    if(l==r){
        tr[ps].sum=z;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)change(tr[ps].ls,l,mid,x,z);
         else change(tr[ps].rs,mid+1,r,x,z);
    updt(ps);
}
void exchange(int &px,int &py,int l,int r,int x,int y){
    if(!px)px=++gs;
    if(!py)py=++gs;
    if(l==x && r==y){
        swap(px,py);
        return;
    }
    int mid=(l+r)>>1;
    if(y<=mid)exchange(tr[px].ls,tr[py].ls,l,mid,x,y);else
    if(x>mid)exchange(tr[px].rs,tr[py].rs,mid+1,r,x,y);else
    exchange(tr[px].ls,tr[py].ls,l,mid,x,mid),
    exchange(tr[px].rs,tr[py].rs,mid+1,r,mid+1,y);
    updt(px);
    updt(py);
}
int main(){
    n=read();
    fo(i,1,n)c[i]=read();
    fo(i,1,n)v[i]=read();
    fo(i,1,n-1){
        x=read(),y=read();
        x++,y++;
        lb(x,y),lb(y,x);
    }
    gs=0;
    dg(1);cnt=gs;
    fo(j,1,19)fo(i,1,n)fa[i][j]=fa[fa[i][j-1]][j-1];
    gs=0;
    fo(i,1,n){
        change(root[c[i]],1,cnt,L[i],v[i]);
        change(root[c[i]],1,cnt,R[i],-v[i]);
    }
    m=read();
    while(m--){
        ch=getchar();
        x=read(),y=read(),z=read();
        if(ch=='A'){
            x++,y++;
            lca=getlca(x,y);
            ans=query(root[z],1,cnt,1,L[x]);
            ans+=query(root[z],1,cnt,1,L[y]);
            ans-=query(root[z],1,cnt,1,L[lca]);
            Write(ans);
            P('\n');
        } else
        if(ch=='C'){
            x++;
            exchange(root[y],root[z],1,cnt,L[x],R[x]);
        } else{
            x++;
            fo(i,0,9)
                if(query(root[i],1,cnt,L[x],L[x])){
                    change(root[i],1,cnt,L[x],0);
                    change(root[i],1,cnt,R[x],0);
                    break;
                }
            change(root[y],1,cnt,L[x],z);
            change(root[y],1,cnt,R[x],-z);
        }
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值