线段树
单点修改
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500005*4; //线段树范围要开4倍
struct Tree{
int l,r,sum,maxx;
};
Tree node[maxn];
int a[maxn];
void PushUp(int i){
node[i].sum = node[i<<1].sum+node[(i<<1)|1].sum;
node[i].maxx = max(node[i<<1].maxx,node[(i<<1)|1].maxx);
}
void build(int i,int l,int r){ //建树
node[i].l = l;node[i].r = r;
if(l==r){
node[i].maxx = a[l];
node[i].sum = a[l];
return;
}
int mid = (l+r)/2;//
build(i<<1,l,mid);
build((i<<1)|1,mid+1,r);
PushUp(i);
}
void add(int i,int k,int v){//单点添加
if(node[i].l==k&&node[i].r==k){
node[i].sum+=v;
node[i].maxx+=v;
return;
}
int mid = (node[i].l+node[i].r)/2;
if(k<=mid)add(i<<1,k,v);
else add((i<<1)|1,k,v);
PushUp(i);
}
int getmax(int i,int l,int r){ //求给定范围最大值
if(node[i].l==l&&node[i].r==r)return node[i].maxx;
int mid = (node[i].l+node[i].r)/2;
if(r<=mid)return getmax(i<<1,l,r);//范围全部都在左边
else if(l>mid)return getmax((i<<1)|1,l,r);//范围全部都在右边
else return max(getmax(i<<1,l,mid),getmax((i<<1)|1,mid+1,r));
}
int getsum(int i, int l, int r){//求给定区间和
if (node[i].l==l&&node[i].r==r)
return node[i].sum;
int mid = (node[i].l+node[i].r)/2;
if(r<=mid) return getsum(i<<1,l,r);
else if(l>mid) return getsum((i<<1)|1,l,r);
else return getsum(i<<1,l,mid)+getsum((i<<1)|1,mid+1,r);
}
int main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++)cin >> a[i];
build(1,1,n);
cout << getsum(1,2,3) << endl;
cout << getmax(1,2,3) << endl;
add(1,3,1);
cout << getsum(1,2,3) << endl;
cout << getmax(1,2,3) << endl;
return 0;
}
区间修改
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 500005*4; //线段树范围要开4倍
struct Tree{
int l,r;
ll sum;
int mid(){
return (l+r)>>1;
}
}tree[maxn];
ll a[maxn];
ll lazy[maxn]; //懒人数组
void PushDown(int i,int m){
if(lazy[i]){
lazy[i<<1]+=lazy[i];
lazy[(i<<1)|1]+=lazy[i];
tree[i<<1].sum+=lazy[i]*(m-(m>>1));
tree[(i<<1)|1].sum+=lazy[i]*(m>>1);
lazy[i]=0;
}
}
void build(int i,int l,int r){
tree[i].l = l;
tree[i].r = r;
lazy[i] = 0;
if(l==r){
tree[i].sum = a[l];
return;
}
int m = tree[i].mid();
build(i<<1,l,m);
build((i<<1)|1,m+1,r);
tree[i].sum = tree[i<<1].sum+tree[(i<<1)|1].sum;
}
void update(int i,ll c,int l,int r){
if(tree[i].l>=l&&tree[i].r<=r){ //若修改的区间大于本区间,一般只会作用在如最大区间为[1,4],而我更新的区间为[0,5]
lazy[i]+=c;
tree[i].sum+=c*(r-l+1);
return;
}
int m = tree[i].mid();
PushDown(i,tree[i].r-tree[i].l+1);
if(r<=m)update(i<<1,c,l,r);
else if(l>m)update((i<<1)|1,c,l,r);
else {
update(i<<1,c,l,m);
update((i<<1)|1,c,m+1,r);
}
tree[i].sum = tree[i<<1].sum+tree[(i<<1)|1].sum;
}
ll getsum(int i,int l,int r){
if(l==tree[i].l&&r==tree[i].r)return tree[i].sum;
int m = tree[i].mid();
PushDown(i,tree[i].r-tree[i].l+1);
ll res = 0;
if(r<=m)res+=getsum(i<<1,l,r);
else if(l>m)res+=getsum((i<<1)|1,l,r);
else {
res+=getsum(i<<1,l,m);
res+=getsum((i<<1)|1,m+1,r);
}
return res;
}
int main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++)cin >> a[i];
build(1,1,n);
cout << getsum(1,2,4) << endl;
update(1,2,2,3);
cout << getsum(1,2,4) << endl;
return 0;
}
树状数组
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500010;
int d[maxn];
int n,m;
inline int lowbit(int x){return -x&x;}
int getsum(int r){
int res = 0;
while(r)res+=d[r],r-=lowbit(r);
return res;
}
void update(int x,int y){
while(x <= n)d[x] += y,x += lowbit(x);
}
int main(){
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i = 1,a; i <= n; i++){
cin >> a;
update(i,a);
}
for(int i = 1; i <= m; i++){
int tp,x,k;
cin >> tp >> x >> k;
if(tp == 1){
update(x,k);
}else{
cout << getsum(k) - getsum(x-1) << '\n';
}
}
return 0;
}
扫描线
#include <bits/stdc++.h>
using namespace std;
const int N = 10010;
int n;
struct Segment{
int x, y1, y2;
int k;
bool operator< (const Segment &t)const{
return x<t.x;
}
}seg[N*2];
struct Node{
int l,r;
int cnt,len; //cnt当前区间被覆盖的次数 ,len当前区间至少被覆盖一次的长度(最大覆盖长度)
}tr[N*4];
void pushup(int u){
if(tr[u].cnt > 0) tr[u].len = tr[u].r - tr[u].l + 1;
else if(tr[u].l == tr[u].r) tr[u].len = 0;
else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
}
void build(int u, int l, int r){ //建树
tr[u] = {l, r};
if(l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
void modify(int u, int l, int r, int k){ //区间修改
if(tr[u].l >= l && tr[u].r <= r) {
tr[u].cnt += k;
pushup(u);
}else{
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u << 1,l,r,k);
if(r > mid) modify(u <<1|1,l,r,k);
pushup(u);
}
}
int main(){
scanf("%d", &n);
int m = 0;
for(int i = 0; i < n; i++){
int x1, y1, x2, y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
seg[m++] = {x1,y1,y2,1}; //第一个边
seg[m++] = {x2,y1,y2,-1}; //第二个边
}
sort(seg, seg + m); //排序
build(1, 0, 10000);
int res = 0;
for(int i = 0; i < m; i++){
if (i > 0) res+=tr[1].len*(seg[i].x-seg[i-1].x);
modify(1, seg[i].y1, seg[i].y2-1, seg[i].k);
}
printf("%d\n", res);
return 0;
}
莫队
#include <bits/stdc++.h>
using namespace std;
#define JS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
typedef long long ll;
const int maxn = 5e4+10;
int a[maxn],pos[maxn],cnt[maxn];
ll ans[maxn],res;
struct Q{
int l,r,k;
}q[maxn];
bool cmp(Q x,Q y){return pos[x.l]==pos[y.l]?x.r<y.r:pos[x.l]<pos[y.l];}
void Add(int n){cnt[a[n]]++;res+=cnt[a[n]]*cnt[a[n]]-(cnt[a[n]]-1)*(cnt[a[n]]-1);}
void Sub(int n){cnt[a[n]]--;res-=(cnt[a[n]]+1)*(cnt[a[n]]+1)-cnt[a[n]]*cnt[a[n]];}
int main(){
JS
int n,m,k;
cin >> n >> m >> k;
int le = sqrt(n);
if(n%le)le++;
for(int i = 1; i <= n; i++)cin >> a[i],pos[i]=i/le;
for(int i = 0; i < m; i++){
cin >> q[i].l >> q[i].r;
q[i].k = i;
}
sort(q,q+m,cmp);
int l = 1,r = 0;
for(int i = 0; i < m; i++){
while(q[i].l < l) Add(--l);
while(q[i].r > r) Add(++r);
while(q[i].l > l) Sub(l++);
while(q[i].r < r) Sub(r--);
ans[q[i].k] = res;
}
for(int i = 0; i < m; i++)cout << ans[i] << endl;
return 0;
}
分块
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1<<20;
int a[N],k,len,n;//a[N]记录数列,k指块的数目,len指块的长度
int L[N],R[N],F[N],add[N],Max[N];
// L[N]记录每个块起始元素的下标,R[N]记录每个块末尾元素下标
// F[N]记录每一个元素所属哪一个块,add[N]加法标记
// Max[N]记录每个区间的最大值
inline void Build(){ //建块
memset(a,0,sizeof(a));
memset(add,0,sizeof(add));
memset(Max,0,sizeof(Max));
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
len = sqrt(n);k = n/len; //计算块的长度和数目
if(n%k) k++; //特判最后一个不完整块
for(int i = 1; i <= k; i++)
R[i] = i*len,L[i]=R[i-1]+1; //计算每一个块的起始末尾元素下标
R[k] = n;
for(int i = 1; i <= k; i++)
for(int j = L[i]; j <= R[i]; j++){
F[j] = i; //计算每一个元素所属哪一个块
Max[i] = max(Max[i],a[j]); //计算该区间最大值
}
}
inline int Ask(int x){return a[x]+add[F[x]];} //单点询问
inline void Add(int x,int y,int z){ //区间加法
if(F[x]==F[y]){ //如果区间被包含于一个整块
for(int i = x; i <= y; i++) a[i]+=z;
return;
}
//如果区间跨过整个块
for(int i = x; i <= R[F[x]]; i++)a[i]+=z;
for(int i = L[F[y]]; i <= y; i++)a[i]+=z;
for(int i = F[x]+1; i < F[y]; i++)add[i]+=z;
}
void update(int x,int v){ //单点更新 只是用于区间求最值 区间添加和区间求最值是不能一起用的
int t = F[x];
a[x] = v;
for(int i = L[t]; i <= R[t]; i++)Max[t] = max(Max[t],a[i]);
}
inline int Quarymax(int x,int y){ //求区间最大值
int ans = -1;
if(F[x]==F[y]){ //如果区间被包含于一个整块
for(int i = x; i <= y; i++)ans = max(ans,a[i]);
return ans;
}
//如果区间跨过整个块
for(int i = x; i <= R[F[x]]; i++)ans = max(ans,a[i]);
for(int i = L[F[y]]; i <= y; i++)ans = max(ans,a[i]);
for(int i = F[x]+1; i < F[y]; i++)ans = max(ans,Max[i]);
return ans;
}
int main(){
cin >> n;
Build();
int aa,bb;
int t = 10;
while(t--){
cin >> aa >> bb;
cout << Quarymax(aa,bb) << endl;
cin >> aa >> bb;
update(aa,bb);
}
return 0;
}
ST表
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int ans=0,flag=1;
char ch=getchar();
while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
if(ch=='-') flag=-1,ch=getchar();
while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
return ans*flag;
}
const int maxn = 1e5+10;
int f[maxn][18]; //18是log2(maxn)大一点的数
int n,m;
int Query(int l, int r){
int d = log2(r-l+1);
return max(f[l][d],f[r-(1<<d)+1][d]);
}
int main(){
n = read(),m = read();
for(int i = 1; i <= n; i++)f[i][0] = read();
for(int j = 1; j <= 17; j++){
for(int i = 1; i + (1 << j) - 1 <= n; i++)
f[i][j] = max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
for(int i = 1, l, r; i <= m; i++){
l = read(),r = read();
printf("%d\n",Query(l,r));
}
return 0;
}