解析·优化 ZKW线段树

德鲁伊!大自然已经听命于我了!
死亡之翼长子奈法利安

ZKW天下第一!
摘自某群聊天记录

ZKW线段树,即非递归形式的线段树,出自终极大犇ZKW的论文《统计的力量》。与普通的线段树相比,ZKW线段树由于是非递归形式,效率极高,代码也极短,成为了OI比赛中极为实用的优化算法之一。虽然ZKW线段树无法处理有运算优先级的线段树问题,但是在一般的问题上和常数偏大的问题上总能带来极强的游戏体验。

ZKW线段树的建树

普通线段树
       1
      / \
     2   3                     <---------------"弱小,可伶又无助"
    / \ 
   4   5
ZKW线段树
         1
       /   \
     10     11                     <---------------"天下第一!"
    / \     / \
  100 101 110 111

那么接下来进入我们的分析环节:小学生找规律
通过观察,我们可以发现:线段树对应叶子节点的下标和原数组的下标的差值是恒定的
事实上,这个值几乎恒等于线段树数组里叶子节点的数量。
事实上,该值num满足:num=2^{[log_2(n+1)]}
于是我们可以先将线段树建为一棵满二叉树,然后我们从叶子节点开始回溯即可。
定义

#define maxn 10000
int n,num;
int minv[maxn<<2];

其中,minv为线段树数组,n为总节点数量,N即为上文提到的N。
那么完整的建树代码如下

inline int ksm(int x,int y){
    int res=1;
    while(y){
        if(y&1)res*=x%p;
        x*=x%p;
        y>>=1;
    }
    return res;
}
inline void build(){
  scanf("%d", &n);
  N=ksm(2,log2(n+1));
  for(register int i=N+1;i<=N+n;i++)cin>>minv[i];
  for(register int i=N-1;i>=1;i--)minv[i]=minv[i<<1]+minv[i<<1|1];
}

ZKW线段树的修改&查询

单点修改与单点查询

代码量很少,背模板即可

单点更新
inline void update(int x,int k){
    for(register int i=x+N;i;i>>=1)tree[i]+=k;
}
区间(单点)查询
inline int query(int l,int r){
    int ans=0;
    for(l=N+l-1,r=N+r+1;s ^ r ^ 1;s>>=1,r>>=1){
        if(~s&1)ans+=tree[s ^ 1];
        if(r&1)ans+=tree[r ^ 1];
    }
    return ans;
}
ZKW线段树单点操作
#include<bits/stdc++.h>
#define maxn 10000
using namespace std;
int n,N;
int a[maxn];
int ansv[maxn<<2];
inline int ksm(int x,int y){
    int res=1;
    while(y){
        if(y&1)res*=x%p;
        x*=x%p;
        y>>=1;
    }
    return res;
}
inline void build(int n){
  N=ksm(2,log2(n+1));
  for(register int i=N+1;i<=N+n;i++)ansv[i]=a[i];
  for(register int i=N-1;i>=1;i--)ansv[i]=ansv[i<<1]+ansv[i<<1|1];
}
inline void update(int x,int k){
    for(register int i=x+N;i;i>>=1)ansv[i]+=k;
}
inline int query(int l,int r){
    int ans=0;
    for(l=N+l-1,r=N+r+1;s ^ r ^ 1;s>>=1,r>>=1){
        if(~s&1)ans+=tree[s ^ 1];
        if(r&1)ans+=tree[r ^ 1];
    }
    return ans;
}

区间修改与区间查询

与普通线段树类似的,我们在ZKW线段树上也不能使用暴力的方式进行区间修改。在ZKW线段树上做暴力修改的复杂度甚至比普通线段树更高。同时,在ZKW线段树中我们仍然需要使用到lazy标记。然而不同的是,在ZKW线段树中我们会将lazy标记永久固化,即永远不对标记做pushdown操作。
我们假定现在指定了一个区间[l,r],使得区间的每一个值全部加上k,并使得l=2,r=10
l到了[2,2]节点时,显然[3,3]节点需要被标记上k,那么接下来我们走到的[2,3]、[0,3]节点都会被标记上k*1,等我们到达[0,7]节点时,因为[4,7]已经被标记了k,所以[0,7]节点的值要加上k*(1+4)=k*5,自然我们需要一个变量来存储k的系数。
需要注意的是,这次的修改要上推到根节点

inline void update(int l,int r,int k) {
    int lNum=0,rNum=0,nNum=1;
    for(l=N+l-1,r=N+r+1;l ^ r ^ 1;l>>=1,r>>=1,nNum<<=1){
        ansv[l]+=k*lNum;
        ansv[r]+=k*rNum;
        if(~s&1){
            lazy[s ^ 1]+=k;
            ansv[s ^ 1]+=k*nNum;
            lNum += nNum;
        }
        if(t&1){
            lazy[t ^ 1]+=k;
            ansv[t ^ 1]+=k*nNum;
            rNum+=nNum;
        }
    }
    for(;l;l>>=1,r>>=1){
        ansv[l]+=k*lNum;
        ansv[r]+=k*rNum;
    }    
}

区间查询的过程类似。

inline int query(int l, int r){
    int lNum=0,rNum=0,nNum=1;
    int ans=0;
    for(l=N+l-1,r=N+r+1;l ^ r ^ 1;l>>=1,r>>=1,nNum<<=1){
        if(lazy[l])ans+=lazy[l]*lNum;
        if(lazy[r])ans+=lazy[r]*rNum;
        if(~l&1){
            ans+=ansv[l ^ 1];
            lNum+=nNum;
        }
        if(r&1){
            ans+=ansv[r ^ 1];
            rNum+=nNum;
        }
    }
    for(;l;l>>=1,r>>=1) {
        ans+=lazy[l]*lNum;
        ans+=lazy[r]*rNum;
    }
    return ans;
}
线段树区间操作代码
#include<bits/stdc++.h>
#define maxn 10000
using namespace std;
int n,N;
int a[maxn];
int ansv[maxn<<2],lazy[maxn<<2];
inline int ksm(int x,int y){
    int res=1;
    while(y){
        if(y&1)res*=x%p;
        x*=x%p;
        y>>=1;
    }
    return res;
}
inline void build(int n){
  N=ksm(2,log2(n+1));
  for(register int i=N+1;i<=N+n;i++)ansv[i]=a[i];
  for(register int i=N-1;i>=1;i--)ansv[i]=ansv[i<<1]+ansv[i<<1|1];
}
inline void update(int l,int r,int k) {
    int lNum=0,rNum=0,nNum=1;
    for(l=N+l-1,r=N+r+1;l ^ r ^ 1;l>>=1,r>>=1,nNum<<=1){
        ansv[l]+=k*lNum;
        ansv[r]+=k*rNum;
        if(~l&1){
            lazy[l ^ 1]+=k;
            ansv[l ^ 1]+=k*nNum;
            lNum += nNum;
        }
        if(r&1){
            lazy[r ^ 1]+=k;
            ansv[r ^ 1]+=k*nNum;
            rNum+=nNum;
        }
    }
    for(;l;l>>=1,r>>=1){
        ansv[l]+=k*lNum;
        ansv[r]+=k*rNum;
    }    
}
inline int query(int l, int r){
    int lNum=0,rNum=0,nNum=1;
    int ans=0;
    for(l=N+l-1,r=N+r+1;l ^ r ^ 1;l>>=1,r>>=1,nNum<<=1){
        if(lazy[l])ans+=lazy[l]*lNum;
        if(lazy[r])ans+=lazy[r]*rNum;
        if(~l&1){
            ans+=ansv[l ^ 1];
            lNum+=nNum;
        }
        if(r&1){
            ans+=ansv[r ^ 1];
            rNum+=nNum;
        }
    }
    for(;l;l>>=1,r>>=1) {
        ans+=lazy[l]*lNum;
        ans+=lazy[r]*rNum;
    }
    return ans;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容