树状数组和线段树
Posted 中二病没有蛀牙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组和线段树相关的知识,希望对你有一定的参考价值。
树状数组
-
某个位置加上一个位置(单点修改) O ( l o g n ) O(log_n) O(logn)
-
求某一个前缀和 (区间查询) O ( l o g n ) O(log_n) O(logn)
c ( x ) = c ( x ) + c ( l o w b i t ( x ) + … … ) c(x) = c(x) + c(lowbit(x)+……) c(x)=c(x)+c(lowbit(x)+……)
l
o
w
b
i
t
(
x
)
=
x
&
−
x
lowbit(x) = x\\&-x
lowbit(x)=x&−x
树状数组公式:
c
(
x
)
=
(
x
−
l
o
w
b
i
t
(
x
)
,
x
]
c(x) = (x - lowbit(x),x]
c(x)=(x−lowbit(x),x]
板子题
https://www.acwing.com/problem/content/1266/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int s[maxn],tr[maxn];
int n,m,a,b,k;
int lowbit(int x){
return x & -x;
}
void add(int x,int v)
{
for(int i = x; i <= n;i += lowbit(i))
tr[i] +=v ;
}
int querry(int x){
int res = 0;
for(int i = x;i ;i -= lowbit(i))
res += tr[i];
return res;
}
int main ()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i= 1;i <= n; ++i)
{
cin>>s[i];
add(i,s[i]);
}
while(m--){
cin>>k>>a>>b;
if(k == 1){
add(a,b);
}
if(k == 0)
cout<<querry(b)-querry(a - 1)<<endl;
}
return 0;
}
经典例题数星星
https://www.acwing.com/problem/content/1267/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 32005;
typedef pair<int ,int> PII;
int tr[maxn];
int n;
int ans[maxn];
int lowbit(int x){
return x & -x;
}
void add(int x,int v)
{
for(int i = x; i < maxn ; i += lowbit(i)){
tr[i] +=v ;
}
}
int querry(int x){
int res = 0;
for(int i = x;i; i-= lowbit(i)){
res += tr[i];
}
return res;
}
int main()
{
int x,y,ny,nx;
cin>>n;
for(int i = 1; i<= n;i++){
cin>>x>>y;
x++;
int t = querry(x);
ans[t]++;
add(x,1);
}
for(int i = 0 ;i < n;i++)
cout<<ans[i]<<endl;
return 0;
}
线段树
- 单点修改
- 区间查询 O ( l o g n ) O(log_n) O(logn)
- 区间修改(懒标记)
操作:
- pushup
- build
- modify
- querry
板子题
和上面的树状数组是同一个题
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int s[maxn];
int n,m;
struct node
{
int l,r;
int sum;
/* data */
}tr[maxn*4];
void pushup(int u){
tr[u].sum = tr[u<<1].sum + tr[u << 1 | 1].sum;
}
void build(int u,int l,int r){
if(l == r) tr[u] = {l,r,s[r]};
else{
tr[u] = {l,r};
int mid = l +r >>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int x,int v)
{
if(tr[u].l == tr[u].r) tr[u].sum += v;
else{
int mid= tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u<<1,x,v);
if(x > mid) modify(u<<1|1,x,v);
pushup(u);
}
}
int querry(int u,int l,int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
int mid = tr[u].l + tr[u].r >>1;
int sum = 0;
if(l <= mid) sum += querry(u<<1,l,r);
if(r > mid) sum += querry(u<<1 |1,l,r);
return sum;
}
int main ()
{
ios::sync_with_stdio(false);
cin>>n>>m;
int k,a,b;
for(int i= 1;i <= n; ++i)
{
cin>>s[i];
}
build(1,1,n);
while(m--){
cin>>k>>a>>b;
if(k == 1){
modify(1,a,b);
}
if(k == 0)
cout<<querry(1,a,b)<<endl;;
}
return 0;
}
数列区间最大值
https://www.acwing.com/problem/content/1272/
线段树维护最大值
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
typedef long long ll ;
int a[maxn];
int n,m;
struct node
{
int l,r;
int ma;
}tr[4*maxn];
void pushup(int u)
{
tr[u].ma = max(tr[u<<1].ma,tr[u<<1 | 1].ma);
}
void build(int u,int l,int r){
if(l == r) tr[u] = {l,r,a[r]};
else{
tr[u] = {l,r};
int mid = l +r >> 1;
build(u << 1,l,mid);
build(u << 1|1,mid+1,r);
pushup(u);
}
}
int querry(int u ,int l,int r){
if( l <= tr[u].l &&r >= tr[u].r) return tr[u].ma;
int mid = tr[u].l+tr[u].r >> 1;
int ma = 0;
if(l <= mid)
ma = querry(u << 1,l,r);
if(r > mid) ma = max(ma,querry(u << 1|1,l,r));
return ma;
}
int main()
{
// ios::sync_with_stdio(false);
cin>>n>>m;
for(int i = 1;i <= n;++i)
scanf("%d",&a[i]);
build(1,1,n);
int x,y;
while (m--)
{
scanf("%d %d",&x,&y);
printf("%d\\n",querry(1,x,y));
}
return 0;
}
以上是关于树状数组和线段树的主要内容,如果未能解决你的问题,请参考以下文章