USACO 2019 February Contest, Gold Problem 1. Cow Land 括号线段树

题目链接:http://usaco.org/index.php?page=viewproblem2&cpid=921

 

题意:

       给你一棵树,树上的每个结点都有一个权值。现在给你两种操作,一个是1 a b,表示将结点a的权值改成b,另一个是2 u v,表示查询从结点u到v路径上(包括u和v)所有点权值的异或和。

做法:

       第一次用括号线段树做出来题目,心里有点激动。

       括号线段树不想解释,解释起来一大堆。概括一下就是在搜索的时候,进入一个节点的时候标记一个点,出来的时候标记一个点,在这次的线段树上,这些点都要被附上这个点的值,有什么好处呢?因为我们的线段树记录的是异或和,所以如果当前这个点不是在我们想要的路径上,那么肯定会要么不进入,那么进入又再次出来了,异或一下就没有值了。更新的话也只需要修改单点即可,查询a到b的路径点权异或和的时候,只要找从根节点到a的值和根节点到b的值进行异或,由于他们的lca会被异或两次所以再把lca的值异或一下就可以了。

       另外,emmm ,由于我们的oj会在深搜的时候爆栈..所以多加了一句话。


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int mod=(int)1e9+7;
const int maxn=100005;
int L[maxn],R[maxn],x[maxn];
int xsum[maxn*10],n,q,cnt,dep[maxn];
int P[maxn*2],fa[maxn][25];
vector<int> ve[maxn];
void dfs(int u,int f){
    L[u]=++cnt;
    P[cnt]=u;
    for(int i=0;i<ve[u].size();i++){
        int v=ve[u][i];
        if(v==f) continue;
        dep[v]=dep[u]+1;
        fa[v][0]=u;
        dfs(v,u);
    }
    R[u]=++cnt;
    P[cnt]=u;
}
void solve(){
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
}
int LCA(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;i--)
        if(dep[fa[x][i]]>=dep[y])
            x=fa[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
void build(int l,int r,int rt){
    if(l==r){
        xsum[rt]=x[P[l]];
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,lson);
    build(mid+1,r,rson);
    xsum[rt]=xsum[lson]^xsum[rson];
}

void update(int l,int r,int rt,int pos,int v){
    if(l==r){
        xsum[rt]=v;
        return ;
    }
    int mid=(l+r)/2;
    if(pos<=mid) update(l,mid,lson,pos,v);
    else update(mid+1,r,rson,pos,v);
    xsum[rt]=xsum[lson]^xsum[rson];
}
int query(int l,int r,int rt,int ql,int qr){
    if(ql<=l&&r<=qr){
        return xsum[rt];
    }
    int mid=(l+r)/2;
    int ret=0;
    if(ql<=mid) ret^=query(l,mid,lson,ql,qr);
    if(qr>mid) ret^=query(mid+1,r,rson,ql,qr);
    return ret;
}
int main(){
    int size = 512 << 20; // 512MB
    char *p = (char*)malloc(size) + size;
    __asm__("movl %0, %%esp\n" :: "r"(p));

    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&x[i]);
    }
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        ve[x].push_back(y);
        ve[y].push_back(x);
    }
    dep[1]=1;
    dfs(1,-1);
    build(1,cnt,1);
    solve();
    while(q--){
        int op,a,b;
        scanf("%d%d%d",&op,&a,&b);
        if(op==1){
            x[a]=b;
            update(1,cnt,1,L[a],b);
            update(1,cnt,1,R[a],b);
        }
        else{
            int lca=LCA(a,b);
            int ans1=query(1,cnt,1,1,L[a]),ans2=query(1,cnt,1,1,L[b]);
            printf("%d\n",ans1^ans2^x[lca]);
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值