“东信杯”广西大学第一届程序设计竞赛(同步赛)J-RMQ(线段树+位运算)

思路来源

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=37515332

代码1

#include<cstdio>
#include<cstring>
 
using namespace std;
typedef long long ll;
const int maxn=200020, maxsz=800020;
 //yh为lazy标记
//sum为节点区间和,其实没必要维护,每次都要统计sigma(cs[i]<<i)的话会退化,查询的时候现加即可
//cs[i]代表该节点所代表[l,r]区间中,第i位为1的个数。
struct pj{ll yh, sum, cs[20];} tr[maxsz];
ll a[maxn], ans;
 
void updata(int x) {
    for (int i=0; i<20; ++i)
        tr[x].cs[i]=tr[x<<1].cs[i]+tr[x<<1|1].cs[i];
}
 
void get_new(int x, int l, int r, int z) {
//要或的z的右起第i位为1
    for (int i=0; i<20; ++i)
        if(z>>i&1) tr[x].cs[i]=(r-l+1);
}
 
void push_down(int x, int l, int r) {
    if(!tr[x].yh) return;
    int mid=(l+r)>>1;
    get_new(x<<1, l, mid, tr[x].yh);
    get_new(x<<1|1, mid+1, r, tr[x].yh);
//下放lazy标记,同时该节点标记清零
    tr[x<<1].yh|=tr[x].yh; tr[x<<1|1].yh|=tr[x].yh;
    tr[x].yh=0;
}
 
void build(int x, int l, int r) {
    if(l==r) {
        for (int i=0; i<20; ++i)
            tr[x].cs[i]=a[l]>>i&1;
        return;
    }
    int mid=(l+r)>>1;
    build(x<<1, l, mid); build(x<<1|1, mid+1, r);
    updata(x);//即pushup操作
}
 
void change(int x, int l, int r, int l0, int r0, int z) {
    if(l0<=l&&r<=r0)//[l,r]完全包含在要更新的[l0,r0]区间内,没有必要下溯了 
    {
        get_new(x, l, r, z);
        tr[x].yh|=z;
        return;
    }
    //[l,r]与[l0,r0]相交,势必用到[l,r]内的值,未更新的[l,r]不能用所以要下推
    push_down(x, l, r);
    int mid=(l+r)>>1;
    //若l0<=mid,即[l,mid]包含l0;同理若r0>mid,即[mid+1,r]包含r0;
    //这说明在子区间里有需要更新的相交段
    //由于[l,r]初始是[1,n]所以根本不会进一个与[l0,r0]完全不相交的区间
    if(l0<=mid) change(x<<1, l, mid, l0, r0, z);
    if(r0>mid) change(x<<1|1, mid+1, r, l0, r0, z);
    updata(x);
}

//同上,很好理解了QAQ
void get_ans(int x, int l, int r, int l0, int r0) {
    if(l0<=l&&r<=r0) {
        for (int i=0; i<20; ++i) ans+=(tr[x].cs[i]<<i);
        return;
    }
    push_down(x, l, r);
    int mid=(l+r)>>1;
    if(l0<=mid) get_ans(x<<1, l, mid, l0, r0);
    if(r0>mid) get_ans(x<<1|1, mid+1, r, l0, r0);
}
 
int main() {
    int n, m; scanf("%d%d", &n, &m);
    for (int i=1; i<=n; ++i) scanf("%lld", a+i);
    build(1, 1, n); ll z; char s[100];
    for (int ty, x, y; m--; ) {
        scanf("%s%d%d", s, &x, &y);
        if(s[0]=='S') {
            ans=0;
            get_ans(1, 1, n, x, y);
            printf("%lld\n", ans);
        } else {
            scanf("%lld", &z);
            change(1, 1, n, x, y, z);
        }
    }
    return 0;
}

代码2

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
ll ans;
int n,m,a[maxn];
int l,r,v;
ll dat[5*maxn][20],cov[5*maxn][20];
char s[10];
void pushup(int p,int i)
{
    dat[p][i]=dat[p<<1][i]+dat[p<<1|1][i];
}
void build(int p,int l,int r,int i)
{
    cov[p][i]=0;
    if(l==r)
    {
        dat[p][i]=(a[l]>>i)&1;
        return;
    }
    int mid=(l+r)/2;
    build(p<<1,l,mid,i);
    build(p<<1|1,mid+1,r,i);
    pushup(p,i);
}
void pushdown(int p,int l,int r,int i)
{
    if(cov[p][i])
    {
        int mid=(l+r)/2;
        dat[p<<1][i]=mid-l+1;
        dat[p<<1|1][i]=r-mid;
        cov[p<<1][i]=1;
        cov[p<<1|1][i]=1;
        cov[p][i]=0;
    }
}
void update(int p,int l,int r,int ql,int qr,int i)
{
    if(ql<=l&&r<=qr)
    {
        dat[p][i]=r-l+1;
        cov[p][i]=1;
        return;
    }
    pushdown(p,l,r,i);
    int mid=(l+r)/2;
    if(ql<=mid)update(p<<1,l,mid,ql,qr,i);
    if(qr>mid)update(p<<1|1,mid+1,r,ql,qr,i);
    pushup(p,i); 
}   
ll ask(int p,int l,int r,int ql,int qr,int i)
{
    if(ql<=l&&r<=qr)return dat[p][i];
    pushdown(p,l,r,i);
    ll res=0;
    int mid=(l+r)/2;
    if(ql<=mid)res+=ask(p<<1,l,mid,ql,qr,i);
    if(qr>mid)res+=ask(p<<1|1,mid+1,r,ql,qr,i);
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    scanf("%d",&a[i]);
    for(int i=0;i<20;++i)
    build(1,1,n,i);
    for(int i=1;i<=m;++i)
    {
        scanf("%s",s);
        if(s[0]=='S')
        {
            scanf("%d%d",&l,&r);
            ans=0;
            for(int i=0;i<20;++i)
            ans+=ask(1,1,n,l,r,i)*(1ll<<i);
            printf("%lld\n",ans);
        }
        else if(s[0]=='O')
        {
            scanf("%d%d%d",&l,&r,&v);
            for(int i=0;i<20;++i)
            if(v>>i&1)update(1,1,n,l,r,i);
        }
    }
    return 0;
}

心得

感谢高中生的代码.jpg

通俗易懂好评,以后要多总结线段树的板子,

平时要多练多总结,线段树板子背不下来没关系,

但是要懂得如何更新、如何打标记的精髓。

笔记都写在代码里了,回头多整理几道线段树的题吧。

感觉自己在线段树方面还是不熟练,

有的时候是更新一些值时,没有认识到其对区间的影响可以被抽象出来。

 

这题就是完成一个或操作,按二进制位存储,开二十棵线段树,

或者按代码,仍然按4*n建一棵树,开20位存,

和x进行或操作时,如果x在第i位上是1,就将第i位在[l,r]赋值为1,

具体操作就是把代表区间的那个节点的值赋为区间长度即r-l+1。

求和的时候按位求和即可。

 

lazy标记的时候,若该区间先或上x1,再或上x2,其等价于或上(x1|x2),

所以标记就不断的或就可以,下放的时候清零。

 

其实是自己记忆力太差背不下来代码

而且有过两天不见代码就改(要)不(重)动(学)板子的倾向

不然才不会写这么多辣鸡注释

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值