【模板】线段树 1
https://www.luogu.com.cn/problem/P3372【模板】线段树 2
https://www.luogu.com.cn/problem/P3373
【模板】树状数组 1https://www.luogu.com.cn/problem/P3374【模板】树状数组 2
https://www.luogu.com.cn/problem/P3368【模板】可持久化线段树 2
https://www.luogu.com.cn/problem/P3834
模板1
单点修改+区间查询
线段树
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+3;
int f[maxn*4],a[maxn];
int n,m;
void buildtree(int k,int l,int r){
if(l==r){
f[k]=a[l];
return;
}
int m=(l+r)>>1;
buildtree(k+k,l,m);
buildtree(k+k+1,m+1,r);
f[k]=f[k+k]+f[k+k+1];
}
void add(int k,int l,int r,int x,int y){
f[k]+=y;
if(l==r)
return;
int m=(l+r)>>1;
if(x<=m)
add(k+k,l,m,x,y);
else
add(k+k+1,m+1,r,x,y);
}
int cale(int k,int l,int r,int s,int t){
if(l==s&&r==t){
return f[k];
}
int m=(l+r)>>1;
if(t<=m)
return cale(k+k,l,m,s,t);
else
if(s>m)
return cale(k+k+1,m+1,r,s,t);
else
return cale(k+k,l,m,s,m)+cale(k+k+1,m+1,r,m+1,t);
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
buildtree(1,1,n);
for(int i=1;i<=m;i++){
int t,x,y;
cin>>t>>x>>y;
if(t==1)
add(1,1,n,x,y);
else
printf("%d\n",cale(1,1,n,x,y));
}
return 0;
}
树状数组
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+6;
#define lowbit(x) x&(-x)
int sum[maxn],a[maxn];
int n,p,l,r;
void add(int x, int d){ //给位置x增加d
for(;x<=n;x+=lowbit(x)) sum[x]+=d;
}
void init(){
n=8;
for(int i=1;i<=n;i++){
a[i]=i;
}
for(int i=1;i<=n;i++){
add(i,a[i]);
}
}
int ask(int x){ //求位置x的前缀和
int res = 0;
for(;x;x-=lowbit(x)) res+=sum[x];
return res;
}
int range_ask(int l, int r){ //区间求和
return ask(r) - ask(l - 1);
}
int main(){
init();
return 0;
}
模板2
区间修改单点查询
树状数组
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+6;
#define lowbit(x) x&(-x)
int sum[maxn],a[maxn];
int n,p,l,r;
void add(int x,int d){
//这个函数用来在树状数组中直接修改
for(;x<=n;x+=lowbit(x)){
sum[x]+=d;
}
}
void range_add(int l,int r,int d){
//给区间[l, r]加上x
add(l,d),add(r+1,-d);
}
int query(int x){
//单点查询(前缀和)
int res=0;
for(;x;x-=lowbit(x)){
res+=sum[x];
}
return res;
}
void init(){
n=8;
for(int i=1;i<=n;i++){
a[i]=i;
}
for(int i=1;i<=n;i++){
add(i,a[i]-a[i-1]);
}
}
int main(){
init();
return 0;
}
模板3
区间修改+区间查询
线段树(P3打标机)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+2;
int n,m,a[100001];
ll f[400001],v[400001];
inline void bulidtree(int k,int l,int r){
v[k]=0;
if(l==r){
f[k]=a[l];
return;
}
int m=(l+r)>>1;
bulidtree(k+k,l,m);
bulidtree(k+k+1,m+1,r);
f[k]=f[k+k]+f[k+k+1];
}
inline void insert(int k,int l,int r,int s,int t,ll d){
if(l==s&&r==t){
v[k]+=d;
return;
}
f[k]+=(t-s+1)*d;
int m=(l+r)>>1;
if(t<=m)
insert(k+k,l,m,s,t,d);
else if(s>m)
insert(k+k+1,m+1,r,s,t,d);
else{
insert(k+k,l,m,s,m,d),
insert(k+k+1,m+1,r,m+1,t,d);
}
}
ll calc(int k,int l,int r,int s,int t,ll p){
p+=v[k];
if(l==s&&r==t){
return p*(r-l+1)+f[k];
}
int m=(l+r)>>1;
if(t<=m)
return calc(k+k,l,m,s,t,p);
else if(s>m)
return calc(k+k+1,m+1,r,s,t,p);
else
return calc(k+k,l,m,s,m,p)+calc(k+k+1,m+1,r,m+1,t,p);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
bulidtree(1,1,n);
while(m--){
int t,x,y;
ll d;
scanf("%d",&t);
if(t==1){
scanf("%d%d%lld",&x,&y,&d);
insert(1,1,n,x,y,d);
}else{
scanf("%d%d",&x,&y);
printf("%lld\n",calc(1,1,n,x,y,0));
}
}
return 0;
}
线段树优化(单标记下传)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[100001];
ll f[400001],v[400001];
void buildtree(int k,int l,int r){
v[k]=0;
if(l==r){
f[k]=a[l];
return;
}
int m=(l+r)>>1;
buildtree(k+k,l,m);
buildtree(k+k+1,m+1,r);
f[k]=f[k+k]+f[k+k+1];
}
void insert(int k,int l,int r,int x,int y,int d){
if(l==x&&r==y){
v[k]+=d;
return;
}
if(v[k])
v[k+k]+=v[k],v[k+k+1]+=v[k],v[k]=0;
int m=(l+r)>>1;
if(y<=m)
insert(k+k,l,m,x,y,d);
else if(x>m)
insert(k+k+1,m+1,r,x,y,d);
else
insert(k+k,l,m,x,m,d),insert(k+k+1,m+1,r,m+1,y,d);
f[k]=f[k+k]+v[k+k]*(m-l+1)+f[k+k+1]+v[k+k+1]*(r-m);
}
ll calc(int k,int l,int r,int x,int y){
if(l==x&&r==y){
return f[k]+v[k]*(r-l+1);
}
if(v[k])
v[k+k]+=v[k],v[k+k+1]+=v[k],v[k]=0;
int m=(l+r)>>1;
ll res=0;
if(y<=m)
res=calc(k+k,l,m,x,y);
else if(x>m)
res=calc(k+k+1,m+1,r,x,y);
else
res=calc(k+k,l,m,x,m)+calc(k+k+1,m+1,r,m+1,y);
f[k]=f[k+k]+v[k+k]*(m-l+1)+f[k+k+1]+v[k+k+1]*(r-m);
return res;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
buildtree(1,1,n);
while(m--){
int t,x,y,d;
scanf("%d",&t);
if(t==1){
scanf("%d%d%d",&x,&y,&d);
insert(1,1,n,x,y,d);
}else{
scanf("%d%d",&x,&y);
printf("%lld\n",calc(1,1,n,x,y));
}
}
return 0;
}
模板4
线段树双标记下传 (区间操作)
当对区间操作运算超过两种,标记下传,以及更新建议在函数中实现
%%%模板4双标记下传可以涵盖模板1,2,3;
树状数组能完成的操作线段树都可以完成。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, p, a[100001];
ll f[400001], v[400001], c[400001];
inline void buildtree(int k, int l, int r) {
v[k] = 0, c[k] = 1;
if (l == r) {
f[k] = a[l] % p;
return;
}
int m = (l + r) / 2;
buildtree(k + k, l, m);
buildtree(k + k + 1, m + 1, r);
f[k] = f[k + k] + f[k + k + 1], f[k] %= p;
}
inline void pushdown(int k, int l, int r) {
c[k + k] *= c[k], c[k + k] %= p;
v[k + k] *= c[k], v[k + k] %= p;
v[k + k] += v[k], v[k + k] %= p;
c[k + k + 1] *= c[k], c[k + k + 1] %= p;
v[k + k + 1] *= c[k], v[k + k + 1] %= p;
v[k + k + 1] += v[k], v[k + k + 1] %= p;
c[k] = 1, v[k] = 0;
}
inline void update(int k, int l, int r) {
int m = (l + r) / 2;
f[k] = f[k + k] * c[k + k] % p + v[k + k] * (m - l + 1) % p + f[k + k + 1] * c[k + k + 1] % p + v[k + k + 1] * (r - m) % p;
f[k] %= p;
}
inline void mul(int k, int l, int r, int x, int y, int z) {
if (l == x && r == y) {
c[k] *= z, c[k] %= p;
v[k] *= z, v[k] %= p;
return;
}
pushdown(k, l, r);
int m = (l + r) / 2;
if (y <= m)
mul(k + k, l, m, x, y, z);
else if (x > m)
mul(k + k + 1, m + 1, r, x, y, z);
else
mul(k + k, l, m, x, m, z), mul(k + k + 1, m + 1, r, m + 1, y, z);
update(k, l, r);
}
inline void add(int k, int l, int r, int x, int y, int z) {
if (l == x && r == y) {
v[k] += z, v[k] %= p;
return;
}
pushdown(k, l, r);
int m = (l + r) / 2;
if (y <= m)
add(k + k, l, m, x, y, z);
else if (x > m)
add(k + k + 1, m + 1, r, x, y, z);
else
add(k + k, l, m, x, m, z), add(k + k + 1, m + 1, r, m + 1, y, z);
update(k, l, r);
}
int calc(int k, int l, int r, int x, int y) {
if (l == x && r == y) {
int res = f[k] * c[k] % p + v[k] * (r - l + 1) % p;
return res % p;
}
pushdown(k, l, r);
int m = (l + r) / 2;
int res = 0;
if (y <= m)
res = calc(k + k, l, m, x, y);
else if (x > m)
res = calc(k + k + 1, m + 1, r, x, y);
else
res = calc(k + k, l, m, x, m) + calc(k + k + 1, m + 1, r, m + 1, y), res %= p;
update(k, l, r);
return res;
}
int main() {
scanf("%d%d%d", &n, &m, &p);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
buildtree(1, 1, n);
for (int i = 1; i <= m; i++) {
int op;
scanf("%d", &op);
if (op == 1) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
mul(1, 1, n, x, y, z);
} else if (op == 2) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
add(1, 1, n, x, y, z);
} else {
int x, y;
scanf("%d%d", &x, &y);
printf("%d\n", calc(1, 1, n, x, y));
}
}
return 0;
}
【模板】可持久化线段树 2
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 1;
struct node {
int l, r, sum;
} hjt[maxn * 40];
int n, m, cnt = 0, a[maxn], root[maxn];
vector<int> v;
int getid(int x) {
return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
inline void insert(int l, int r, int pre, int &now, int p) {
hjt[++cnt] = hjt[pre];
now = cnt;
hjt[now].sum++;
if (l == r)
return;
int m = (l + r) >> 1;
if (p <= m)
insert(l, m, hjt[pre].l, hjt[now].l, p);
else
insert(m + 1, r, hjt[pre].r, hjt[now].r, p);
}
int query(int l, int r, int L, int R, int k) {
if (l == r)
return l;
int m = (l + r) >> 1;
int temp = hjt[hjt[R].l].sum - hjt[hjt[L].l].sum;
if (k <= temp)
return query(l, m, hjt[L].l, hjt[R].l, k);
else
return query(m + 1, r, hjt[L].r, hjt[R].r, k - temp);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), v.push_back(a[i]);
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
for (int i = 1; i <= n; i++)
insert(1, n, root[i - 1], root[i], getid(a[i]));
while (m--) {
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", v[query(1, n, root[l - 1], root[r], k) - 1]);
}
return 0;
}