普及向 ZKW线段树!

啊,是否疲倦了现在的线段树

太弱,还递归!

那我们就欢乐的学习另外一种神奇的线段树吧!(雾

他叫做zkw线段树

这个数据结构灰常好写(虽然线段树本身也特别好写……)

速度快(貌似只在单点更新方面比线段树快……)

是一种自底向上非递归版本的线段树!

首先我们来看一个ppt,《统计的力量》这个是发明人的PPT(啊,ppt内的代码是错的……

統計的力量

好吧,我们来写吧~

首先预备条件:

int M,T[maxn*2+2];

M指的是什么呢?M就指的是这颗zkw线段树最下面的那个点之前的编号是什么

T数组就是这个zkw线段树的数组,由于zkw线段树是一颗满二叉树,所以直接开两倍就好啦~

接下来我们就来建树吧!

以单点更新,区间查询和作为一个例子~

void build(int x)
{
    for(M=1;M<=n+1;M<<1);

    for(int i=1;i<=n;i++)
        scanf("%d",&T[i+M]);

    for(int i=M-1;i;i--)
        T[i]=T[i<<1]+T[i<<1|1];
}

很显然这是一颗非常弱的从底往上更新的树!

然后updata怎么写呢?qsc仔细想了想,很简单

void updata(int n,int val)
{
    T[n+=M]=val;//这个地方是单点修改的哟
    for(n>>=1;n;n>>=1)
        T[n]=T[n<<1]+T[n<<1|1];
}

哇,其实和线段树是一个意思,直接顺着节点直接往上爬就是了!

query肿么写呢?还是很简单呀

int query(int l,int r)
{
    int ans=0;
    l=l+M-1,r=r+M+1;
    for(;l^r^1;l>>1,r>>=1)
    {
        if(~l&1)ans+=T[l^1];
        if(r&1) ans+=T[r^1];
    }
    return ans;
}

这个乱七八糟的位运算什么意思呀?

l^r^1的意思,就是左边的这个点和右边这个点是否互为兄弟,或者干脆就是一个点

~l&1 就是判断这个左边这个是否为左儿子,r&1判断这个节点是否为右二子

如果是的话,那就得加上他的兄弟咯~

啊,zkw单点更新区间查询就示范到这儿了,是不是灰常简单呀~

我们首先来搞一道例题:

HDU 1166 敌兵布阵 单点更新,区间查询

//qscqesze
#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <typeinfo>
#include <fstream>
#include <map>
#include <stack>
typedef long long ll;
using namespace std;
//freopen("D.in","r",stdin);
//freopen("D.out","w",stdout);
#define sspeed ios_base::sync_with_stdio(0);cin.tie(0)
#define maxn 200001
#define mod 10007
#define eps 1e-9
//const int inf=0x7fffffff;   //无限大
const int inf=0x3f3f3f3f;
/*

int buf[10];
inline void write(int i) {
  int p = 0;if(i == 0) p++;
  else while(i) {buf[p++] = i % 10;i /= 10;}
  for(int j = p-1; j >=0; j--) putchar(‘0‘ + buf[j]);
  printf("\n");
}
*/
//**************************************************************************************
inline ll read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

ll T[maxn*4];
int M,n;
void build()
{
    for(M=1;M<=n+1;M<<=1);
    for(int i=1;i<=n;i++)
        T[i+M]=read();
    for(int i=M-1;i;i--)
        T[i]=T[i<<1]+T[i<<1|1];
}

void updata(int x,int val)
{
    T[x+=M]+=val;
    for(x>>=1;x>=1;x>>=1)
    {
        T[x]=T[x<<1]+T[x<<1|1];
    }
}

ll query(int l,int r)
{
    l=l+M-1,r=r+M+1;
    ll ans=0;
    for(;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1)ans+=T[l^1];
        if(r&1) ans+=T[r^1];
    }
    return ans;
}

int main()
{
    int cas=0;
    int t=read();
    string s;
    for(int cas=1;cas<=t;cas++)
    {
        memset(T,0,sizeof(T));
        printf("Case %d:\n",cas);
        n=read();
        build();
        while(cin>>s)
        {
            if(s[0]==‘E‘)
                break;
            int a,b;
            a=read(),b=read();
            if(s[0]==‘A‘)
                updata(a,b);
            else if(s[0]==‘S‘)
                updata(a,-b);
            else
                printf("%lld\n",query(a,b));
        }
    }
}
时间: 2024-10-19 09:14:18

普及向 ZKW线段树!的相关文章

【bzoj3685】普通van Emde Boas树 权值zkw线段树

原文地址:http://www.cnblogs.com/GXZlegend/p/6809743.html 题目描述 设计数据结构支持:1 x  若x不存在,插入x2 x  若x存在,删除x3    输出当前最小值,若不存在输出-14    输出当前最大值,若不存在输出-15 x  输出x的前驱,若不存在输出-16 x  输出x的后继,若不存在输出-17 x  若x存在,输出1,否则输出-1 输入 第一行给出n,m 表示出现数的范围和操作个数接下来m行给出操作n<=10^6,m<=2*10^6,

ZKW线段树

对于区间问题,我们常用的方法是线段树.递归式的线段树具有通用性,但速度太慢.ZKW神犇使用非递归的线段树,常数特别小. 与大部分线段树一样,ZKW线段树采用堆式存储.也就是说,x节点的左儿子是x*2,右儿子是x*2+1,父亲是x/2. 由于采用非递归,我们要方便地找到叶子节点.ZKW线段树的方法是,从小到大枚举叶子节点数,直到线段树装得下.比如建立1000个节点,他从1开始枚举,2,4,8,16,--1024.发现1024足够大时,将[1, 1024)作为非叶节点,[1024, 2048)为叶子

回档|忠诚2|zkw线段树

线段树,大家一看就明白了吧,这题就是一模板题,我学zkw线段树用的.单点维护,区间最值. 题目是tyvj的忠诚2. #include"iostream" #include"cstdio" using namespace std; int M; int T[10000000]; int read() { char c=getchar(); int a=0; while (c<'0'||c>'9') c=getchar(); while (c>='0'

【模板】zkw线段树の基本操作

整理了一下zkw线段树的基本操作: 1.修改元素值:给定x,y,修改a[x]的值为y: 2.查询元素值:给定x,输出a[x]的值: 3.区间增减:给定l,r,x,将从a[l]到a[r](含al和ar),即区间[l,r]中的每个值都加上x: 4.区间求和:给定l,r,输出从a[l]到a[r](含al和ar),即区间[l,r]中的每个元素之和. #include<cstdio> #include<cstdlib> #include<cctype> #include<c

【题解】CODEVS 1217 借教室 zkw线段树(区间最值)

捣鼓了好长时间才终于把zkw线段树的区间最值修改给A掉了 = =zkw神犇的课件里竟然有错! (╯▽╰)下面的伪代码里被注释的是我修改的>< Func Add_x(s,t,x) for (s=s+M-1,t=t+M+1;s^t^1;s>>=1,t>>=1) { if (~s&1) T[s^1]+=x; if ( t&1) T[t^1]+=x; A=min(T[s],T[s^1]),T[s]-=A,T[s^1]-=A, T[s>>1]+=A;

【POJ3468】【zkw线段树】A Simple Problem with Integers

Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval. In

BZOJ3196 二逼平衡树 ZKW线段树套vector(滑稽)

我实在是不想再打一遍树状数组套替罪羊树了... 然后在普通平衡树瞎逛的时候找到了以前看过vector题解 于是我想:为啥不把平衡树换成vector呢??? 然后我又去学了一下ZKW线段树 就用ZKW线段树套vector水过啦!!! 每个ZKW线段树的节点保存一个vector 操作1在分出的vector上查询比它小的数有多少个然后相加再加1 操作2二分再上操作1 操作3修改需要修改的节点的vector 操作4在分出vector上查询前驱取最大 操作5与操作4同理 luogu主站5772ms上卡过,

[模板]树状数组1/ZKW线段树

https://www.luogu.org/problemnew/show/P3374 1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 #define rson (o<<1|1) 5 #define lson (o<<1) 6 const int N = 530000<<1;//zkw线段树只能查询(0,bit),所以只有bit-2个叶节点,bit-2<n

ZKW线段树 非递归版本的线段树

学习和参考 下面是支持区间修改和区间查询的zkw线段树模板,先记下来. #include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <iomanip> #include <cstdlib> #include <cstdio> #include <string> #include <vect