ZKW线段树之旅(1)

膜拜ZKW神犇。。。。Orz。。。

ZKW线段树的思想是直接找到一个大区间对应的小区间
由于是自底向上的,所以常数很小(《统计的力量》)
其实我觉得,真正难写 的是区间修改

比如现在造一颗线段树,支持区间求和,区间修改
ZKW线段树有两种做法:
一是维护神奇 的前缀和的前缀和
二是沿用递归版线段树的思路,在 区间上打标记,然后统计

太弱,第一种不会。。。于是我就用第二种吧
区间修改:简单,就在区间上打个标记,顺便更新一下遍历到的区间和,最后当l和r父亲相同时,循环结束,再把他们的父亲们的和更新。
区间求和:先是记一下左边右边 的区间里有几个元素要加,每次就根据遍历到的区间的标记更新答案。最后当l和r父亲相同时,循环结束,再把他们的父亲们的标记拿来更新答案

好像有点大话连篇。。。上代码好了

const int maxn=262144;
int sumv[maxn],addv[maxn],m;
//sumv 维护区间的和,addv是标记;m不说了吧,地球人都知道
void update(int l,int r,int a){
    l+=m-1,r+=m+1;
    int ln=0,rn=0;//记录l,r遍历到了几个元素
    int s=1;//记录这一层的区间有几个元素
    for(;l^r^1;l>>=1,r>>=1,s<<=1){
        if(ln)sumv[l]+=ln*a;
        if(rn)sumv[r]+=rn*a;
        if(~l&1)addv[l^1]+=a,ln+=s;//只更新标记啊
        if(r&1)addv[r^1]+=a,rn+=s;
    }
    data[l]+=ln*a;data[r]+=rn*a;
    for(ln+=rn,l>>=1;l;l>>=1)
        data[l]+=ln*a;//我的父亲们~
}
int query(int l,int r){
    l+=m-1,r+=m+1;
    int sum=0;
    int ln=0,rn=0;
    for(int s=1;l^r^1;l>>=1,r>>=1,s<<=1){
        if(ln)sum+=addv[l]*ln;//根据标记更新和
        if(rn)sum+=addv[r]*rn;
        if(~l&1)sum+=data[l^1]+addv[l^1]*s,ln+=s;
        if(r&1)sum+=data[r^1]+addv[r^1]*s,rn+=s;
    }
    sum+=addv[l]*ln+addv[r]*rn;
    for(ln+=rn,l>>=1;l;l>>=1)
        sum+=addv[l]*ln;//我的父亲们~
    return sum;
}
//PS:有错误就说一说啊。。。


现在,某人又要求造一颗线段树支持 区间修改,区间最小值
还是沿用区间求和的思路,只是代码有点不同
对于更新最小值的操作,不能单纯的加减了,而是要根据儿子的值而修改。

const int maxn=262144;
int data[maxn],addv[maxn],m,n;
//data是区间最小
int query(int l,int r){
    int lmax=INT_MAX,rmax=INT_MAX;
    int s=l+m-1,t=r+m+1;
    bool _l=false,_r=false;
    //叶节点不能通过儿子更新,判断是否可以更新
    //其实好像下面有更好的判断方式。。。懒得改了
    for(;s^t^1;s>>=1,t>>=1){
        if(_l)lmax+=addv[s];
        if(_r)rmax+=addv[t];
        if(~s&1)lmax=min(lmax,data[s^1]+addv[s^1]),_l=true;
        if(t&1)rmax=min(rmax,data[t^1]+addv[t^1]),_r=true;
    }
    lmax+=addv[s];rmax+=addv[t];
    lmax=min(lmax,rmax);
    for(s>>=1;s;s>>=1)lmax+=addv[s];
    return lmax;
}
void add(int l,int r,int a){
    int s=l+m-1,t=r+m+1;
    for(;s^t^1;s>>=1,t>>=1){
            if(s<l+m-1)data[s]=min(data[s<<1]+addv[s<<1],data[s<<1|1]+addv[s<<1|1]);        
            if(t<r+m+1)data[t]=min(data[t<<1]+addv[t<<1],data[t<<1|1]+addv[t<<1|1]);
            if(~s&1)addv[s^1]+=a;
            if(t&1)addv[t^1]+=a;
        }
        data[t]=min(data[t<<1]+addv[t<<1],data[t<<1|1]+addv[t<<1|1]);
        for(;s;s>>=1)
            data[s]=min(data[s<<1]+addv[s<<1],data[s<<1|1]+addv[s<<1|1]);
    }


嘿嘿嘿。。。
我的线段树~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值