2017暑假训练第十九天

  网站坏了....所以今天决定看看线段树的博客,也是边看边写边理解吧。

  首先还是敌兵布阵这道对于线段树和树状数组都比较基础的题目:

  之前用它写过树状数组的模板,现在决定还是用这个题写写线段树的模板(最基础的单点更新,区间查询)。

 

#include<iostream>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<cstdio>
using namespace std;
int sum[200010];
int pushup(int x){
    return sum[x*2]+sum[x*2+1];
}
void build(int id,int l,int r){
    if (l==r){
        scanf("%d",sum[id]);
        return ;
    }
    else {
        int mid=(l+r)/2;
        build (id*2,l,mid);
        build (id*2+1,mid+1,r);
        pushup(id);
    }
}
int query(int ql,int qr,int id,int l,int r){
    if (ql<=l&&r<=qr){
        return sum[id];
    }
    int mid=(l+r)/2;
    int ans=0;
    if (ql<=mid)ans+=query(ql,qr,id*2,l,mid);
    else ans+=quert(ql,qr,id*2+1,mid+1,r);
    return ans;
}
void update(int x,int y,int id,int l,int r){
    if (l==r){
        sum[id]+=y;
        return ;
    }
    else {
        int mid=(l+r)/2;
        if (id<=mid){
            update(id,y,id*2,l,mid);
        }
        else {
            update(id,y,id*2+1,mid+1,r);
        }
        pushup(id);
    }
}
int main()
{
    int t;
    int i,j,k,l;
    scanf("%d",&t);
    for(k=1;k<=t;k++)
    {
        printf("Case %d:\n",k);

        int n;
        scanf("%d",&n);
        build(1,1,n);
        char str[20];
        int u,v;
        while(scanf("%s",str)==1&&str[0]!='E'){
            scanf("%d%d",&u,&v);
            if(str[0]=='Q') printf("%d\n",query(u,v,1,1,n));
            else if(str[0]=='A') update(u,v,1,1,n);
            else update(u,-v,1,1,n);
        }
    }
    return 0;
}

之后网站恢复了,做了一道二维树状数组的题和一个离散化+树状数组求逆序的题。

 因为看到两个题都是有关与区间更新的,所以就看了一下比较有名的Lazy标记大法。

 Lazy标记法的原理就是若正好处理到要查询的区间范围,即t[id].l==l&&t[id].r==r,则只对根节点处的值进行更新,并lazy记录,然后return(这样是一步剪枝的操作,避免了不必要的重复操作),然后之后再用到子节点的时候再用记录的数进行更新,也就是常说的pushdown函数。

 相关题目的ac代码如下:

#include<iostream>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<cstdio>
using namespace std;
struct tree{
    long long int l;
    long long int r;
    int mid(){
        return (l+r)/2;
    }
};
struct tree t[400010];
long long int sum[400010];
long long int a[400010];
long long int n;
long long int q;
void pushup(long long int id){
    sum[id]=sum[id*2]+sum[id*2+1];
}
void pushdown(long long int id,long long int m){
    if (a[id]){
        a[id*2]+=a[id];
        a[id*2+1]+=a[id];
        sum[id*2]+=a[id]*(m-m/2);
        sum[id*2+1]+=a[id]*(m/2);
        a[id]=0;
    }
}
void build(long long int id,long long int l,long long int r){
    t[id].l=l;
    t[id].r=r;
    if (l==r){
        scanf("%lld",&sum[id]);
        return ;
    }
    long long int m=t[id].mid();
    build(id*2,l,m);
    build(id*2+1,m+1,r);
    pushup(id);
}
void update(long long int id,long long int l,long long int r,long long int val){
    if (t[id].l==l&&t[id].r==r){
        a[id]+=val;
        sum[id]+=val*(r-l+1);
        return ;
    }
    if (t[id].l==t[id].r)return ;
    pushdown(id,t[id].r-t[id].l+1);
    long long int m=t[id].mid();
    if (r<=m)update(id*2,l,r,val);
    else if (l>m)update(id*2+1,l,r,val);
    else {
        update(id*2,l,m,val);
        update(id*2+1,m+1,r,val);
    }
    pushup(id);
}
long long int query(long long int id,long long int l,long long int r){
    if (t[id].l==l&&t[id].r==r){
        return sum[id];
    }
    pushdown(id,t[id].r-t[id].l+1);
    long long int m=t[id].mid();
    long long int ans=0;
    if (r<=m)ans+=query(id*2,l,r);
    else if (l>m)ans+=query(id*2+1,l,r);
    else {
        ans+=query(id*2,l,m)+query(id*2+1,m+1,r);
    }
    return ans;
}
int main(){
    long long int i,j,k,l;
    long long int xx,yy,zz;
    char fun[2];
    while (scanf("%lld%lld",&n,&q)!=EOF){
        build(1,1,n);
        while (q--){
            scanf("%s",&fun);
            if (fun[0]=='Q'){
                scanf("%lld%lld",&xx,&yy);
                printf("%lld\n",query(1,xx,yy));
            }
            else if (fun[0]=='C'){
                scanf("%lld%lld%lld",&xx,&yy,&zz);
                update(1,xx,yy,zz);
            }
        }
    }
}

  然后发现了一道类似的更新区间的题,但是继续用线段树就tle了,因为这个题是区间更新,单点查询,时间复杂度较高,所以投机取巧的选择了树状数组的另类处理方式(思路借鉴的前两天在博客里看到的题,题意大概是区间内的球涂色,问涂了几次,这个题意大概是区间开花,问有几个开花,理解了就发现这两个其实就是一个题的变式),对树状数组稍加写写就完成了这个题。

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值