ZKW线段树入门
Part 1
来说说它的构造
线段树的堆式储存
我们来转成二进制看看
小学生问题:找规律
规律是很显然的
- 一个节点的父节点是这个数左移1,这个位运算就是低位舍弃,所有数字左移一位
- 一个节点的子节点是这个数右移1,是左节点,右移1+1是右节点
- 同一层的节点是依次递增的,第n层有2^(n-1)个节点
- 最后一层有多少节点,值域就是多少(这个很重要)
有了这些规律就可以开始着手建树了
- 查询区间[1,n]
最后一层不是2的次幂怎么办?
开到2的次幂!后面的空间我不要了!就是这么任性!
Build函数就这么出来了!找到不小于n的2的次幂
直接输入叶节点的信息
int n,M,q;int d[N<<];
inline void Build(int n){
for(M=;M<n;M<<=);
for(int i=M+;i<=M+n;i++) d[i]=in();
}
建完了?当然没有!父节点还都是空的呢!
维护父节点信息?
倒叙访问,每个节点访问的时候它的子节点已经处理过辣!
- 维护区间和?
for(int i=M-;i;--i) d[i]=d[i<<]+d[i<<|];
- 维护最值?
1 for(int i=M-;i;--i) d[i]=max/min (d[i<<],d[i<<|]);
这样就构造出了一颗二叉树,也就是zkw线段树了!
如果你是压行选手的话(比如我),建树的代码只需要两行。
是不是特别Easy!
新技能Get√
Part 2
单点操作
- 单点修改
void Change(int x,int v){
d[M+x]+=v;
}
只是这么简单?当然不是,跟线段树一样,我们要更新它的父节点!
void Change(int x,int v){
d[x=M+x]+=v;
while(x) d[x>>=]=d[x<<]+d[x<<|];
}
没了?没了。
- 单点查询(差分思想,后面会用到)
把d维护的值修改一下,变成维护它与父节点的差值(为后面的RMQ问题做准备)
建树的过程就要修改一下咯!
void Build(int n){
for(M=;M<=n+;M<<=);for(int i=M+;i<=M+n;i++) d[i]=in();
for(int i=M-;i;--i) d[i]=min(d[i<<],d[i<<|]),d[i<<]-=d[i],d[i<<|]-=d[i];
}
在当前情况下的查询
void Sum(int x,int res=){
while(x) res+=d[x],x>>=;return res;
}
Part 3
区间操作
询问区间和,把[s,t]闭区间换成(s,t)开区间来计算
int Sum(int s,int t,int Ans=){
for (s=s+M-,t=t+M+;s^t^;s>>=,t>>=){
if(~s&) Ans+=d[s^];
if( t&) Ans+=d[t^];
}return Ans;
}
- 为什么
~s&1
? 为什么
t&1
?
变成开区间了以后,如果s是左儿子,那么它的兄弟节点一定在区间内,同理,如果t是右儿子,那么它的兄弟节点也一定在区间内!
这样计算不会重复吗?
答案是会的!所以注意迭代的出口s^t^1
如果s,t就是兄弟节点,那么也就迭代完成了。
代码简单,即使背过也不难QuQ
- 区间最小值
void Sum(int s,int t,int L=,int R=){
for(s=s+M-,t=t+M+;s^t^;s>>=,t>>=){
L+=d[s],R+=d[t];
if(~s&) L=min(L,d[s^]);
if(t&) R=min(R,d[t^]);
}
int res=min(L,R);while(s) res+=d[s>>=];
}
差分!
不要忘记最后的统计!
还有就是建树的时候是用的最大值还是最小值,这个一定要注意,影响到差分。
- 区间最大值
void Sum(int s,int t,int L=,int R=){
for(s=s+M-,t=t+M+;s^t^;s>>=,t>>=){
L+=d[s],R+=d[t];
if(~s&) L=max(L,d[s^]);
if(t&) R=max(R,d[t^]);
}
int res=max(L,R);while(s) res+=d[s>>=];
}
同理。
- 区间加法
void Add(int s,int t,int v,int A=){
for(s=s+M-,t=t+M+;s^t^;s>>=,t>>=){
if(~s&) d[s^]+=v;if(t&) d[t^]+=v;
A=min(d[s],d[s^]);d[s]-=A,d[s^]-=A,d[s>>]+=A;
A=min(d[t],d[t^]);d[t]-=A,d[t^]-=A,d[t>>]+=A;
}
while(s) A=min(d[s],d[s^]),d[s]-=A,d[s^]-=A,d[s>>=]+=A;
}
同样是差分!差分就是厉害QuQ
zkw线段树小试牛刀(code来自hzwer.com)
#include<cstdio>
#include<iostream>
#define M 261244
using namespace std;
int tr[];
void query(int s,int t)
{
int ans=;
for(s=s+M-,t=t+M+;s^t^;s>>=,t>>=)
{
if(~s&)ans+=tr[s^];
if(t&)ans+=tr[t^];
}
printf("%d\n",ans);
}
void change(int x,int y)
{
for(tr[x+=M]+=y,x>>=;x;x>>=)
tr[x]=tr[x<<]+tr[x<<|];
}
int main()
{
int n,m,f,x,y;
scanf("%d",&n);
for(int i=;i<=n;i++){scanf("%d",&x);change(i,x);}
scanf("%d",&m);
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&f,&x,&y);
if(f==)change(x,y);
else query(x,y);
}
return ;
}
POJ3468(code来自网络)
#include <cstdio>
#include <cstring>
#include <cctype>
#define N ((131072 << 1) + 10) //表示节点个数->不小于区间长度+2的最小2的正整数次幂*2+10
typedef long long LL;
inline int getc() {
static const int L = << ;
static char buf[L] , *S = buf , *T = buf;
if (S == T) {
T = (S = buf) + fread(buf , , L , stdin);
if (S == T)
return EOF;
}
return *S++;
}
inline int getint() {
static char c;
while(!isdigit(c = getc()) && c != '-');
bool sign = (c == '-');
int tmp = sign ? : c - '';
while(isdigit(c = getc()))
tmp = (tmp << ) + (tmp << ) + c - '';
return sign ? -tmp : tmp;
}
inline char getch() {
char c;
while((c = getc()) != 'Q' && c != 'C');
return c;
}
int M; //底层的节点数
int dl[N] , dr[N]; //节点的左右端点
LL sum[N]; //节点的区间和
LL add[N]; //节点的区间加上一个数的标记
#define l(x) (x<<1) //x的左儿子,利用堆的性质
#define r(x) ((x<<1)|1) //x的右儿子,利用堆的性质
void pushdown(int x) { //下传标记
if (add[x]&&x<M) {//如果是叶子节点,显然不用下传标记(别忘了)
add[l(x)] += add[x];
sum[l(x)] += add[x] * (dr[l(x)] - dl[l(x)] + );
add[r(x)] += add[x];
sum[r(x)] += add[x] * (dr[r(x)] - dl[r(x)] + );
add[x] = ;
}
}
int stack[] , top;//栈
void upd(int x) { //下传x至根节点路径上节点的标记(自上而下,用栈实现)
top = ;
int tmp = x;
for(; tmp ; tmp >>= )
stack[++top] = tmp;
while(top--)
pushdown(stack[top]);
}
LL query(int tl , int tr) { //求和
LL res=;
int insl = , insr = ; //两侧第一个有用节点
for(tl=tl+M-,tr=tr+M+;tl^tr^;tl>>=,tr>>=) {
if (~tl&) {
if (!insl)
upd(insl=tl^);
res+=sum[tl^];
}
if (tr&) {
if(!insr)
upd(insr=tl^)
res+=sum[tr^];
}
}
return res;
}
void modify(int tl , int tr , int val) { //修改
int insl = , insr = ;
for(tl=tl+M-,tr=tr+M+;tl^tr^;tl>>=,tr>>=) {
if (~tl&) {
if (!insl)
upd(insl=tl^);
add[tl^]+=val;
sum[tl^]+=(LL)val*(dr[tl^]-dl[tl^]+);
}
if (tr&) {
if (!insr)
upd(insr=tr^);
add[tr^]+=val;
sum[tr^]+=(LL)val*(dr[tr^]-dl[tr^]+);
}
}
for(insl=insl>>;insl;insl>>=) //一路update
sum[insl]=sum[l(insl)]+sum[r(insl)];
for(insr=insr>>;insr;insr>>=)
sum[insr]=sum[l(insr)]+sum[r(insr)]; }
inline void swap(int &a , int &b) {
int tmp = a;
a = b;
b = tmp;
}
int main() {
//freopen("tt.in" , "r" , stdin);
int n , ask;
n = getint();
ask = getint();
int i;
for(M = ; M < (n + ) ; M <<= );
for(i = ; i <= n ; ++i)
sum[M + i] = getint() , dl[M + i] = dr[M + i] = i; //建树
for(i = M - ; i >= ; --i) { //预处理节点左右端点
sum[i] = sum[l(i)] + sum[r(i)];
dl[i] = dl[l(i)];
dr[i] = dr[r(i)];
}
char s;
int a , b , x;
while(ask--) {
s = getch();
if (s == 'Q') {
a = getint();
b = getint();
if (a > b)
swap(a , b);
printf("%lld\n" , query(a , b));
}
else {
a = getint();
b = getint();
x = getint();
if (a > b)
swap(a , b);
modify(a , b , x);
}
}
return ;
}
可持久化线段树版本(来自http://blog.csdn.net/forget311300/article/details/44306265)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#define mp(x,y) make_pair(x,y) using namespace std; const int N = ;
const int inf = 0x3f3f3f3f; int a[N + ];
int b[N + ];
int M;
int lq, rq;
vector<pair<int, int> > s[N * ]; void add(int id, int cur)
{
cur += M;
int lat = ;
if (s[cur].size())
lat = s[cur][s[cur].size() - ].second;
s[cur].push_back(mp(id, ++lat));
for (cur >>= ; cur; cur >>= )
{
int l = ;
if (s[cur << ].size())
l = s[cur << ][s[cur << ].size() - ].second;
int r = ;
if (s[cur << | ].size())
r = s[cur << | ][s[cur << | ].size() - ].second;
s[cur].push_back(mp(id, l + r));
}
} int Q(int id, int k)
{
if (id >= M) return id - M;
int l = id << , r = l ^ ;
int ll = lower_bound(s[l].begin(), s[l].end(), mp(lq, inf)) - s[l].begin() - ;
int rr = lower_bound(s[l].begin(), s[l].end(), mp(rq, inf)) - s[l].begin() - ;
int kk = ;
if (rr >= )kk = s[l][rr].second;
if (ll >= )kk = s[l][rr].second - s[l][ll].second;
if (kk < k)return Q(r, k - kk);
return Q(l, k);
} int main()
{
int n, m;
while (~scanf("%d%d", &n, &m))
{
for (int i = ; i < n; i++)
{
scanf("%d", a + i);
b[i] = a[i];
}
sort(b, b + n);
int nn = unique(b, b + n) - b;
for (M = ; M < nn; M <<= );
for (int i = ; i < M + M; i++)
{
s[i].clear();
//s[i].push_back(mp(0, 0));
}
for (int i = ; i < n; i++)
{
int id = lower_bound(b, b + nn, a[i]) - b;
add(i + , id);
}
while (m--)
{
int k;
scanf("%d %d %d", &lq, &rq, &k);
lq--;
int x = Q(, k);
printf("%d\n", b[x]);
}
}
return ;
}
完全模板(来自http://blog.csdn.net/forget311300/article/details/44306265)
const int N = 1e5; struct node
{
int sum, d, v;
int l, r;
void init()
{
d = ;
v = -;
}
void cb(node ls, node rs)
{
sum = ls.sum + rs.sum;
l = ls.l, r = rs.r;
}
int len()
{
return r - l + ;
}
void V(int x)
{
sum = len() * x;
d = ;
v = x;
}
void D(int x)
{
sum += len() * x;
d += x;
}
}; struct tree
{
int m, h;
node g[N << ];
void init(int n)
{
for (m = h = ; m < n + ; m <<= , h++);
int i = ;
for (; i <= m; i++)
{
g[i].init();
g[i].sum = ;
}
for (; i <= m + n; i++)
{
g[i].init();
scanf("%d", &g[i].sum);
g[i].l = g[i].r = i - m;
}
for (; i < m + m; i++)
{
g[i].init();
g[i].sum = ;
g[i].l = g[i].r = i - m;
}
for (i = m - ; i > ; i--)
g[i].cb(g[i << ], g[i << | ]);
}
void dn(int x)
{
for (int i = h - ; i > ; i--)
{
int f = x >> i;
if (g[f].v != -)
{
g[f << ].V(g[f].v);
g[f << | ].V(g[f].v);
}
if (g[f].d)
{
g[f << ].D(g[f].d);
g[f << | ].D(g[f].d);
}
g[f].v = -;
g[f].d = ;
}
}
void up(int x)
{
for (x >>= ; x; x >>= )
{
if (g[x].v != -)continue;
int d = g[x].d;
g[x].d = ;
g[x].cb(g[x << ], g[x << | ]);
g[x].D(d);
}
}
void update(int l, int r, int x, int o)
{
l += m - , r += m + ;
dn(l), dn(r);
for (int s = l, t = r; s ^ t ^ ; s >>= , t >>= )
{
if (~s & )
{
if (o)
g[s ^ ].V(x);
else
g[s ^ ].D(x);
}
if (t & )
{
if (o)
g[t ^ ].V(x);
else
g[t ^ ].D(x);
}
}
up(l), up(r);
}
int Q(int l, int r)
{
int ans = ;
l += m - , r += m + ;
dn(l), dn(r);
for (int s = l, t = r; s ^ t ^ ; s >>= , t >>= )
{
if (~s & )ans += g[s ^ ].sum;
if (t & )ans += g[t ^ ].sum;
}
return ans;
}
};
二维情况(来自http://blog.csdn.net/forget311300/article/details/44306265)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream> using namespace std; const int W = ; int m; struct tree
{
int d[W << ];
void o()
{
for (int i = ; i < m + m; i++)d[i] = ;
}
void Xor(int l, int r)
{
l += m - , r += m + ;
for (int s = l, t = r; s ^ t ^ ; s >>= , t >>= )
{
if (~s & )d[s ^ ] ^= ;
if (t & )d[t ^ ] ^= ;
}
} } g[W << ]; void chu()
{
for (int i = ; i < m + m; i++)
g[i].o();
} void Xor(int lx, int ly, int rx, int ry)
{
lx += m - , rx += m + ;
for (int s = lx, t = rx; s ^ t ^ ; s >>= , t >>= )
{
if (~s & )g[s ^ ].Xor(ly, ry);
if (t & )g[t ^ ].Xor(ly, ry);
}
} int Q(int x, int y)
{
int ans = ;
for (int xx = x + m; xx; xx >>= )
{
for (int yy = y + m; yy; yy >>= )
{
ans ^= g[xx].d[yy];
}
}
return ans;
} int main()
{
int T;
cin >> T;
int fl = ;
while (T--)
{
if (fl)
{
printf("\n");
}
fl = ;
int N, M;
cin >> N >> M;
for (m = ; m < N + ; m <<= );
chu();
while (M--)
{
char o[];
scanf("%s", o);
if (*o == 'Q')
{
int x, y;
scanf("%d%d", &x, &y);
printf("%d\n", Q(x, y));
}
else
{
int lx, ly, rx, ry;
scanf("%d%d%d%d", &lx, &ly, &rx, &ry);
Xor(lx, ly, rx, ry);
}
}
}
return ;
}
非递归扫描线+离散化(来自http://blog.csdn.net/forget311300/article/details/44306265)
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath> using namespace std; const int N = ; int n;
vector<double> y; struct node
{
double s;
int c;
int l, r;
void chu(double ss, int cc, int ll, int rr)
{
s = ss;
c = cc;
l = ll, r = rr;
}
double len()
{
return y[r] - y[l - ];
}
} g[N << ];
int M; void init(int n)
{
for (M = ; M < n + ; M <<= );
g[M].chu(, , , );
for (int i = ; i <= n; i++)
g[i + M].chu(, , i, i);
for (int i = n + ; i < M; i++)
g[i + M].chu(, , n, n);
for (int i = M - ; i > ; i--)
g[i].chu(, , g[i << ].l, g[i << | ].r);
} struct line
{
double x, yl, yr;
int d;
line() {}
line(double x, double yl, double yr, int dd): x(x), yl(yl), yr(yr), d(dd) {}
bool operator < (const line &cc)const
{
return x < cc.x || (x == cc.x && d > cc.d);
}
}; vector<line>L; void one(int x)
{
if (x >= M)
{
g[x].s = g[x].c ? g[x].len() : ;
return;
}
g[x].s = g[x].c ? g[x].len() : g[x << ].s + g[x << | ].s;
} void up(int x)
{
for (; x; x >>= )
one(x);
} void add(int l, int r, int d)
{
if (l > r)return;
l += M - , r += M + ;
for (int s = l, t = r; s ^ t ^ ; s >>= , t >>= )
{
if (~s & )
{
g[s ^ ].c += d;
one(s ^ );
}
if (t & )
{
g[t ^ ].c += d;
one(t ^ );
}
}
up(l);
up(r);
} double sol()
{
y.clear();
L.clear();
for (int i = ; i < n; i++)
{
double lx, ly, rx, ry;
scanf("%lf %lf %lf %lf", &lx, &ly, &rx, &ry);
L.push_back(line(lx, ly, ry, ));
L.push_back(line(rx, ly, ry, -));
y.push_back(ly);
y.push_back(ry);
}
sort(y.begin(), y.end());
y.erase(unique(y.begin(), y.end()), y.end());
init(y.size());
sort(L.begin(), L.end());
n = L.size() - ;
double ans = ;
for (int i = ; i < n; i++)
{
int l = upper_bound(y.begin(), y.end(), L[i].yl + 1e-) - y.begin();
int r = upper_bound(y.begin(), y.end(), L[i].yr + 1e-) - y.begin() - ;
add(l, r, L[i].d);
ans += g[].s * (L[i + ].x - L[i].x);
}
return ans;
} int main()
{
int ca = ;
while (cin >> n && n)
{
printf("Test case #%d\nTotal explored area: %.2f\n\n", ca++, sol());
}
return ;
}
ZKW线段树入门的更多相关文章
- 线段树简单入门 (含普通线段树, zkw线段树, 主席树)
线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...
- 有趣的 zkw 线段树(超全详解)
zkw segment-tree 真是太棒了(真的重口味)!写篇博客纪念入门 emmm...首先我们来介绍一下 zkw 线段树这个东西(俗称 "重口味" ,与 KMP 类似,咳咳. ...
- ZKW线段树
简介 zkw线段树虽然是线段树的另一种写法,但是本质上已经和普通的递归版线段树不一样了,是一种介于树状数组和线段树中间的存在,一些功能上的实现比树状数组多,而且比线段树好写且常数小. 普通线段树采用从 ...
- zkw线段树详解
转载自:http://blog.csdn.net/qq_18455665/article/details/50989113 前言 首先说说出处: 清华大学 张昆玮(zkw) - ppt <统计的 ...
- BZOJ3173 TJOI2013最长上升子序列(Treap+ZKW线段树)
传送门 Description 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? Input ...
- 【POJ3468】【zkw线段树】A Simple Problem with Integers
Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. On ...
- HDU 4366 Successor(树链剖分+zkw线段树+扫描线)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=4366 [题目大意] 有一个公司,每个员工都有一个上司,所有的人呈树状关系,现在给出每个人的忠诚值和 ...
- [SinGuLaRiTy] ZKW线段树
[SinGuLaRiTy-1007] Copyrights (c) SinGuLaRiTy 2017. All Rights Reserved. 关于ZKW线段树 Zkw线段树是清华大学张昆玮发明非递 ...
- 数据结构3——浅谈zkw线段树
线段树是所有数据结构中,最常用的之一.线段树的功能多样,既可以代替树状数组完成"区间和"查询,也可以完成一些所谓"动态RMQ"(可修改的区间最值问题)的操作.其 ...
随机推荐
- IDEA项目找不到浏览器报错的情况
调tomcat的时候,它会调用浏览器,浏览器关联如果有问题,肯定是会报错的 要是测试的时候,就是浏览器的问题,重新把浏览器装一遍让他自己重新关联一下应该就行了
- servlet保存会话数据---利用隐藏域
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletExcep ...
- 【C++ Primer 第13章】2. 拷贝控制和资源管理
拷贝控制和资源管理 • 类的行为像一个值.意味着它应该有自己的状态,当我们拷贝一个像值得对象时,副本和原对象是完全独立的,改变副本不会对原对象有任何影响. • 行为像指针的类则共享状态.当我们拷贝一个 ...
- dubbo支持哪些通信协议和序列化协议
dubbo支持的通信协议 dubbo协议 dubbo://192.168.0.1:20188 默认就是走dubbo协议的,单一长连接,NIO异步通信,基于hessian作为序列化协议 适用的场景就是: ...
- [转] web前端js构造无法销毁的类UUID识别码,识别浏览器设备唯一性
用户行为统计在如今的前端生态中已是稀松寻常,如各种站长统计工具.识别用户访问客户端唯一性是必要的实现,对于web前端获取的设备信息,一般容易想到的是通过navigator.userAgent,但相同设 ...
- Thinkphp框架网站 nginx环境 访问页面access denied
今日不熟一个tiinkphp框架网站的时候,由于服务器环境是centos6.5+nginx1.8,已经运行php商城项目很正常, 本以为一切比较简单,直接新建了项目文件夹,xftp上传了程序,并配置n ...
- JMeter通过自定义jar调用和BeanShell源码
自定义jar包引用 原始java代码,代码的作用的是根据指定的字符串,生成执行长度的随机字符串 package com; import java.util.Random; public class r ...
- net core体系-web应用程序-4asp.net core2.0 项目实战(1)-2项目说明和源码下载
本文目录1. 摘要2. Window下运行 3.linux下运行4. 开发记录5. 总结 1.概要 写<Asp.Net Core 2.0 项目实战>系列断断续续已经很长时间了,期间很多朋友 ...
- 使用ycsb对hbase1.4.9 benchmark
Ycsb下载地址:https://github.com/brianfrankcooper/YCSB/releases 目前测试hbase1.4.9,因此下载ycsb-hbase14-binding-0 ...
- 关系网络数据可视化:2. Python数据预处理
将数据中导演与演员的关系整理出来,得到导演与演员的关系数据,并统计合作次数 import numpy as np import pandas as pd import matplotlib.pyplo ...