线段树与树状数组模版及专题练习

专题练习:

http://acm.hdu.edu.cn/webcontest/contest_show.php?cid=1441

密码是: cugbacm

 

 

首先是准备工作,以下是常见的线段树初始定义:

#include<cstring>
#include<algorithm>
using namespace std;
typedef __int64 lld;
#define ls rt<<1
#define rs rt<<1|1
#define lson l,m,ls
#define rson m+1,r,rs
#define test(a,b) for(int ii=0;ii<(b);ii++)printf("%d%c",(a)[ii],ii==(b)-1?'\n':' ') 
#define cntd(a,b) unique((a),(a)+(b))-(a) 
#define lb(a,b,c) lower_bound((a),(a)+(b),(c))-(a) 
//加入三个函数,test测试,cntd统计不同元素个数,lb找第一个>=c的数的位置。 
const int N = 100010;
//线段树N<<2 


/*
    说明:
    unique(num,mun+n)返回的是num去重后的尾地址,并不是真正把重复的元素删除,而
    是,该函数把重复的元素移到后面去了,依然保存到了原数组中,函数返回去重后最
    后一个元素的地址,因为unique去除的是相邻的重复元素,所以一般用之前都会要排
    一下序。
    
    lower_bound(num,num+n,x)返回的是num前n个数中出现的第一个“>=x”的数的位置。
    
    注意:这两个函数用之前都要排序。
    
    使用:
    test(key,n); //测试数组key前n个数(key[0~n-1])的值;
    sort(key,key+tot); 
    tot=cntd(key,tot);//统计key[0~tot-1]中不重复元素个数;
    pos=lb(key,tot,x[i]);//在key[0~tot-1]中找到第一个“>= x[i]”的元素的位置;
    
    */ 

然后要有一个意识,线段树的[l,r]可能值是多少,如为:key[0~tot-1];(tot为不重复元素个数),用

key记录的是排好序的元素值,然后是线段树:sum[N<<2]记录的则是每段的值,[l,r]对应key[]中[l,r];

 

线段树函数一般格式:

一般地,询问段是pushup ,询问点是pushdown,视题目而定。

一、加点问段型:

void pushup(int rt){ //向上更新
    for(int i=0;i<5;i++){
        s[rt][i]=s[ls][i]+s[rs][(i-sum[ls]%5+5)%5];
    }
}

void build(int l,int r,int rt){//建树
     if(l==r){
         num[rt]=a[l]; 
         return;
     }
     int m=(r+l)>>1;
     build(lson);
     build(rson);
     num[rt]=num[ls]+num[rs];//递归完后将值传给根节点,这句相当于pushup
}

void add(int x,int y,int l,int r,int rt){//对点操作
     if(l==r){
         if(x==l) num[rt]+=y;//到了叶子节点
         return;
     }
     int m=(r+l)>>1;
     if(x<=m) add(x,y,lson);
     if(x>=m+1)  add(x,y,rson);
     num[rt]=num[ls]+num[rs];
}

void query(int ql,int qr,int l,int r,int rt){//询问段
    if(ql<=l && qr>=r){
        ans+=num[rt];//所有分段的和,ans是全局变量
        return;
    }
    int m=(r+l)>>1;
    if(ql<=m) query(ql,qr,lson);
    if(qr>=m+1) query(ql,qr,rson);
}


二、加段问点型:

void pushdown(int rt){ //向下更新
     if(aby[rt]!=0){
         aby[ls]+=aby[rt];
         aby[rs]+=aby[rt];
         aby[rt]=0;
     }
}

void build(int l, int r ,int rt){//建树
     aby[rt]=0;//初始化
     if(l==r){
         aby[rt]=num[l];
         return;
     }
     int m=(l+r)>>1;
     build(lson);
     build(rson);
}

void updata(int ql,int qr,int val,int l,int r,int rt){//对段更新
     if(ql<=y[l] && qr>=y[r]){
         aby[rt]+=val;//操作段覆盖了树中段则更新,再往下传
         return;
     }
     pushdown(rt);
     int m=(l+r)>>1;
     if(ql<=y[m]) updata(ql,qr,val,lson);
     if(qr>=y[m+1]) updata(ql,qr,val,rson);
}

int query(int yy,int l,int r,int rt){//询问点
     if(l==r) return rt;//寻找yy在树中的位置
     pushdown(rt);
     pushdown(rt);
     int m=(l+r)>>1;
     if(yy<=y[m]) return query(yy,lson);
     return query(yy,rson);
}


树状数组也有类似的操作:

一、加点问段:

void add(int i,int j){ //加点
     while(i<N){
         sum[i]+=j;
         i+=(i&(-i));
     }
}
int query(int i){ //问段[a,b] = query(a)-query(b-1)
    int ans=0;
    while(i>0){
        ans+=sum[i];
        i-=(i&(-i));
    }
    return ans;
}


二、加段问点则为:

void add(int i,int x){//加段[a,b]+x :  add(a-1,-1); add(b,1);
     while(i>0){
         sum[i]+=x;
         i-=(i&(-i));
     }
}
int query(int i){ //问点 = query(i) + ori[i]; 
     int ans=0;
     while(i<N){
         ans+=sum[i];
         i+=(i&(-i));
     }
     return ans;
}


 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值