分块入门(根据hzwer的博客。。)(右端点是r不是n。。)

1.区间更新单点查询

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 100005
int n, len, pos[maxn], a[maxn];
struct Block {
    int add;
} b[1000];
void update(int l, int r, int c) {
    int p = pos[l], q = pos[r];
    if (p == q)
        for (int i = l; i <= r; i++) a[i] += c;
    else {
        for (int i = p + 1; i <= q - 1; i++) b[i].add += c;
        for (int i = l; i <= p * len; i++) a[i] += c;
        for (int i = (q - 1) * len + 1; i <= r; i++) a[i] += c;
    }
}
int main() {
    cin >> n;
    len = sqrt(n);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) pos[i] = (i - 1) / len + 1;
    int opt, l, r, c;
    for (int i = 1; i <= n; i++) {
        scanf("%d%d%d%d", &opt, &l, &r, &c);
        if (opt == 0)
            update(l, r, c);
        else
            cout << a[r] + b[pos[r]].add << '
';
    }
}

2.区间求小于c的数的个数:先预处理,即把每块的元素sort一下,整块修改用add标记,查询用lower_bound,两端修改后用sort维护,查询暴力统计即可

注意要把add当做块的属性,而不要下放到块中的元素里去

/*
预处理块时需要将块内元素进行排序,然后可以每次查询都是用二分来做
区间更新时要将两端元素进行单独修改+排序
*/
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 50005
struct Block {
    int add;
    vector<int> v;
} b[5005];
int n, pos[maxn], len;
int a[maxn];

inline void calc(int p) {
    b[p].v.clear();
    for (int i = (p - 1) * len + 1; i <= min(p * len, n); i++) b[p].v.push_back(a[i]);
    sort(b[p].v.begin(), b[p].v.end());
}
void update(int l, int r, int c) {
    int p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) a[i] += c;
        calc(p);
        return;
    }
    for (int i = p + 1; i <= q - 1; i++) b[i].add += c;
    for (int i = l; i <= min(p * len, n); i++) a[i] += c;
    for (int i = (q - 1) * len + 1; i <= r; i++) a[i] += c;
    calc(p);
    calc(q);
}
int query(int l, int r, int c) {
    int res = 0;
    int p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++)
            if (a[i] + b[p].add < c)
                res++;
        return res;
    }
    for (int i = p + 1; i <= q - 1; i++) {
        int x = c - b[i].add;
        int tmp = lower_bound(b[i].v.begin(), b[i].v.end(), x) - b[i].v.begin();
        res += tmp;
    }
    for (int i = l; i <= min(p * len, n); i++)
        if (a[i] + b[p].add < c)
            res++;
    for (int i = (q - 1) * len + 1; i <= r; i++)
        if (a[i] + b[q].add < c)
            res++;
    return res;
}
int main() {
    cin >> n;
    len = sqrt(n);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        pos[i] = (i - 1) / len + 1;
        b[pos[i]].v.push_back(a[i]);
    }
    for (int i = 1; i <= pos[n]; i++)  //预处理先排序
        sort(b[i].v.begin(), b[i].v.end());
    int m = n;
    while (m--) {
        ll opt, l, r, c;
        scanf("%d%d%d%d", &opt, &l, &r, &c);
        if (opt == 0)
            update(l, r, c);
        else
            cout << query(l, r, c * c) << endl;
    }
}

3.区间更新+找区间内小于c的最大的数(c的前驱)

块设为sqrt(n)就会t,设为1000就没事。。玄学复杂度啊

/*
set每次修改都是logn,那么n次块内更新耗时n*sqrt(n)*logsqrt(n)
*/
#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
struct Block {
    int add;
    set<int> s;
} b[1005];
int n, pos[maxn], len, a[maxn];
set<int>::iterator it;

void update(int l, int r, int c) {
    int p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) {
            b[p].s.erase(a[i]);
            a[i] += c;
            b[p].s.insert(a[i]);
        }
        return;
    }
    for (int i = p + 1; i <= q - 1; i++) b[i].add += c;
    for (int i = l; i <= min(p * len, n); i++) {
        b[p].s.erase(a[i]);
        a[i] += c;
        b[p].s.insert(a[i]);
    }
    for (int i = (q - 1) * len + 1; i <= r; i++) {
        b[q].s.erase(a[i]);
        a[i] += c;
        b[q].s.insert(a[i]);
    }
}
int query(int l, int r, int c) {
    int res = -1, p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) {
            int x = a[i] + b[p].add;
            if (x < c)
                res = max(res, x);
        }
        return res;
    }
    for (int i = p + 1; i <= q - 1; i++) {  //大块里用set查
        int x = c - b[i].add;
        it = b[i].s.lower_bound(x);
        if (it == b[i].s.begin())
            continue;
        it--;
        res = max(res, (*it) + b[i].add);
    }
    for (int i = l; i <= min(p * len, n); i++)
        if (a[i] + b[p].add < c)
            res = max(res, a[i] + b[p].add);
    for (int i = (q - 1) * len + 1; i <= r; i++)
        if (a[i] + b[q].add < c)
            res = max(res, a[i] + b[q].add);
    return res;
}

int main() {
    cin >> n;
    len = 1500;
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) {
        pos[i] = (i - 1) / len + 1;
        b[pos[i]].s.insert(a[i]);
    }
    for (int i = 1; i <= n; i++) {
        int opt, l, r, c;
        scanf("%d%d%d%d", &opt, &l, &r, &c);
        if (opt == 0)
            update(l, r, c);
        else
            cout << query(l, r, c) << endl;
    }
}

4.区间更新,区间求和

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 50005
struct Block {
    ll add, sum;
} b[500];
int n, pos[maxn], len;
ll a[maxn];

void update(int l, int r, int c) {
    int p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) a[i] += c, b[p].sum += c;
        return;
    }
    for (int i = p + 1; i <= q - 1; i++) b[i].add += c;
    for (int i = l; i <= min(p * len, n); i++) a[i] += c, b[p].sum += c;
    for (int i = (q - 1) * len + 1; i <= r; i++) a[i] += c, b[q].sum += c;
}
ll query(int l, int r, int c) {
    ll res = 0;
    int p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) res += a[i] + b[p].add;
        return res % c;
    }
    for (int i = p + 1; i <= q - 1; i++) res += b[i].sum, res += len * b[i].add;
    for (int i = l; i <= min(p * len, n); i++) res += b[p].add, res += a[i];
    for (int i = (q - 1) * len + 1; i <= r; i++) res += b[q].add, res += a[i];
    return res % c;
}

int main() {
    cin >> n;
    len = sqrt(n);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        pos[i] = (i - 1) / len + 1;
        b[pos[i]].sum += a[i];
    }
    for (int i = 1; i <= n; i++) {
        int opt, l, r, c;
        scanf("%d%d%d%d", &opt, &l, &r, &c);
        if (opt == 0)
            update(l, r, c);
        else
            cout << query(l, r, c + 1) << endl;
    }
}

 5.区间开根(类似于势能线段树)

/*
2*2=4,4*4=16,16*16=256,每个数最多被开4次,开了4次以上就是1
所以两端暴力开,整块判断块中所有元素是不是1,是的话那么这个块就不用开了
修改最多4n,查询就是n*根号n
*/
#include <bits/stdc++.h>
using namespace std;
#define maxn 50005
struct Block {
    int flag;
    int sum;
} b[500];
int n, len, pos[maxn], a[maxn];

void calc(int p) {
    if (b[p].flag)
        return;
    b[p].flag = 1;
    b[p].sum = 0;
    for (int i = (p - 1) * len + 1; i <= p * len; i++) {
        a[i] = (int)sqrt(a[i]);
        b[p].sum += a[i];
        if (a[i] > 1)
            b[p].flag = 0;
    }
}
void update(int l, int r) {
    int p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) {
            b[p].sum -= a[i];
            a[i] = sqrt(a[i]);
            b[p].sum += a[i];
        }
        return;
    }
    for (int i = p + 1; i <= q - 1; i++) calc(i);
    for (int i = l; i <= min(r, p * len); i++) {
        b[p].sum -= a[i];
        a[i] = sqrt(a[i]);
        b[p].sum += a[i];
    }
    for (int i = (q - 1) * len + 1; i <= r; i++) {
        b[q].sum -= a[i];
        a[i] = sqrt(a[i]);
        b[q].sum += a[i];
    }
}
int query(int l, int r) {
    int res = 0, p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) res += a[i];
        return res;
    }
    for (int i = p + 1; i <= q - 1; i++) res += b[i].sum;
    for (int i = l; i <= min(p * len, r); i++) res += a[i];
    for (int i = (q - 1) * len + 1; i <= r; i++) res += a[i];
    return res;
}

int main() {
    cin >> n;
    len = sqrt(n);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        pos[i] = (i - 1) / len + 1;
        b[pos[i]].sum += a[i];
    }
    for (int i = 1; i <= n; i++) {
        int opt, l, r, c;
        cin >> opt >> l >> r >> c;
        if (opt == 0)
            update(l, r);
        else
            cout << query(l, r) << endl;
    }
}

6.块状数组,块状链表:用来解决在数列中插入一个数

/*
块内维护一个数组和size
再用前缀和log根号n 的时间内找到新来的数应该加在哪个块中 
然后每增加一个数都要消耗该数所在块的长度的时间
如果数据随机,那么一次插入就是O(根号n)

如果不随机。。可能n个数都加在同一个块内,那么会导致复杂度退化为n^2 
那么当块过大时进行重构即可,这里将块大小上限设置为20*根号n 
*/
#include<bits/stdc++.h>
#include<vector>
using namespace std;
#define maxn 200005
struct Block{
    vector<int>v;
}b[20005];
int nn,n,len,m,pos[maxn],a[maxn];//块的长度,块的个数 

void rebuild(){
    nn=0;
    for(int i=1;i<=m;i++){
        for(int j=0;j<b[i].v.size();j++)
            a[++nn]=b[i].v[j];
        b[i].v.clear();
    }
    len=sqrt(nn);m=(nn-1)/len+1;
    for(int i=1;i<=nn;i++){
        pos[i]=(i-1)/len+1;
        b[pos[i]].v.push_back(a[i]);
    }
}
void update(int pos,int x){//在位置pos插入x 
    int p=1;
    while(b[p].v.size()<pos)
        pos-=b[p].v.size(),p++;
    b[p].v.insert(b[p].v.begin()+pos-1,x);
    if(b[p].v.size()>20*len)
        rebuild();
}
int query(int pos){
    int p=1;
    while(b[p].v.size()<pos)
        pos-=b[p].v.size(),p++;
    return b[p].v[pos-1];
}
int main(){
    cin>>n;len=sqrt(n);
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/len+1;
        b[pos[i]].v.push_back(a[i]);
    }
    m=pos[n];
    for(int i=1;i<=n;i++){
        int opt,l,r,c;
        cin>>opt>>l>>r>>c;
        if(opt==0)update(l,r);
        else cout<<query(r)<<endl;
    }
}

 7.标记下传顺序(标记优先级)

/*
把数字表示成a*mul+add的形式 
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define mod 10007
#define ll long long 
struct Block{
    int mul,add;
}b[1005];
int a[maxn],n,len,pos[maxn];

void pushdown(int p){
    for(int i=(p-1)*len+1;i<=min(p*len,n);i++)
        a[i]=((ll)a[i]*b[p].mul+b[p].add)%mod;
    b[p].add=0;b[p].mul=1;
}/*
void add(int l,int r,int c){
    int p=pos[l],q=pos[r];
    if(p==q){
        pushdown(p);
        for(int i=l;i<=r;i++)
            a[i]=(a[i]+c)%mod;
        return;    
    }
    for(int i=p+1;i<=q-1;i++)
        b[i].add=(b[i].add+(ll)b[i].mul*c)%mod;
    pushdown(p);pushdown(q);
    for(int i=l;i<=min(p*len,r);i++)
        a[i]=(a[i]+c)%mod;
    for(int i=(q-1)*len+1;i<=r;i++)
        a[i]=(a[i]+c)%mod;
}
void mul(int l,int r,int c){
    int p=pos[l],q=pos[r];
    if(p==q){
        pushdown(p);
        for(int i=l;i<=r;i++)
            a[i]=((ll)a[i]*c)%mod;
        return;
    }
    for(int i=p+1;i<=q-1;i++){
        b[i].mul=((ll)b[i].mul*c)%mod;
        b[i].add=((ll)b[i].add*c)%mod;
    }
    pushdown(p);pushdown(q);
    for(int i=1;i<=min(p*len,r);i++)
        a[i]=((ll)a[i]*c)%mod;
    for(int i=(q-1)*len+1;i<=r;i++)
        a[i]=((ll)a[i]*c)%mod;
}*/
void update(int f,int l,int r,int c){
    int p=pos[l],q=pos[r];
    pushdown(p);
    for(int i=l;i<=min(r,p*len);i++){
        if(f==0)a[i]+=c;
        else a[i]*=c;
        a[i]%=mod;
    }
    if(p!=q){
        pushdown(q);
        for(int i=(q-1)*len+1;i<=r;i++){
            if(f==0)a[i]+=c;
            else a[i]*=c;
            a[i]%=mod;
        }    
    }
    for(int i=p+1;i<=q-1;i++){
        if(f==0)
            b[i].add=(b[i].add+c)%mod;
        else {
            b[i].mul=(b[i].mul*c)%mod;
            b[i].add=(b[i].add*c)%mod;
        }
    }
}
int query(int x){
    int p=pos[x];
    return ((ll)a[x]*b[p].mul+b[p].add)%mod;
}
int main(){
    cin>>n;len=sqrt(n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        pos[i]=(i-1)/len+1;
    for(int i=1;i<=pos[n];i++)
        b[i].mul=1;
    for(int i=1;i<=n;i++){
        int opt,l,r,c;
        scanf("%d%d%d%d",&opt,&l,&r,&c);
        if(opt==2)cout<<query(r)<<endl;
        else update(opt,l,r,c);
    }
}

 8.区间覆盖

/*
区间众数
覆盖标记
 
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
struct Block{
    int c; 
}b[500];
int a[maxn],n,pos[maxn],len; 
void pushdown(int p){
    if(b[p].c!=-1){
        for(int i=(p-1)*len+1;i<=p*len;i++)
            a[i]=b[p].c;
        b[p].c=-1;
    }
}
int query(int l,int r,int c){
    int res=0,p=pos[l],q=pos[r];
    if(p==q){
        pushdown(p);
        for(int i=l;i<=r;i++)
            if(a[i]==c)res++;
            else a[i]=c;
        return res;
    } 
    pushdown(p);pushdown(q);
    for(int i=l;i<=min(p*len,r);i++)
        if(a[i]==c)res++;
        else a[i]=c;
        
    for(int i=(q-1)*len+1;i<=r;i++)
        if(a[i]==c)res++;
        else a[i]=c;
        
    for(int i=p+1;i<=q-1;i++)
        if(b[i].c!=-1){
            if(b[i].c==c)res+=len;
            else b[i].c=c;
        }
        else {
            for(int j=(i-1)*len+1;j<=i*len;j++)
                if(a[j]==c)res++;
                else a[j]=c;
            b[i].c=c;
        }
     return res;
}

int main(){
    cin>>n;len=sqrt(n);
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)
        pos[i]=(i-1)/len+1,b[pos[i]].c=-1;
    for(int i=1;i<=n;i++){
        int l,r,c;
        cin>>l>>r>>c;
        cout<<query(l,r,c)<<endl;
    } 
}
原文地址:https://www.cnblogs.com/zsben991126/p/10575330.html