分块练习

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Viscu/article/details/82051839

6277. 数列分块入门 1

题面: 传送门

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)

#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int maxn = 1000;
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int k;
int a[50005],b[50005],tag[50005];

void add(int l,int r,int c){
    for(int i=l;i<=min(r,b[l]*k);++i){  //左边不完整块暴力
        a[i]+=c;
    }
    if(b[l]!=b[r]){
        for(int i=(b[r]-1)*k+1;i<=r;++i){  //中间块标记一下+c
            a[i]+=c;
        }
    }
    for(int i=b[l]+1;i<=b[r]-1;++i){ //右边不完整块暴力
        tag[i]+=c;
    }
}
int main(){
    //fin;
    int n=read();
    k=sqrt(n);
    for(int i=1;i<=n;++i){
        a[i]=read();
        b[i]=(i-1)/k+1;  //记录该位置处于哪个块
    }
    for(int i=0;i<n;++i){
        int op=read(),l=read(),r=read(),x=read();
        if(0==op){
            add(l,r,x);
        }else{
            printf("%d\n",a[r]+tag[b[r]]);
        }
    }
    return 0;
}

6278. 数列分块入门 2

题面:传送门
分块思路:首先呢,分成sqrt(n)个块。由于该题目需要查比c*c小的数的个数,
那么我们可以利用二分,所有我们需要预处理每个块,排序一下。
执行add操作时:我们分为三步,左边不完整的块添加c,直接暴力改变值。
中间完整的块我们就做标记,tag[i]表示第i块增加的数量。
右边不完整的块也是暴力改变值即可。左右两个不完整的块改变值后需要对所处的块进行
排序保持其单调性。
执行query操作时,左右不完整块直接暴力查找比c-tag[i]小的即可。
完整的块利用二分即可。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)

#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 1000000000+7;
const int maxn = 1000;
using namespace std;

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int n,k;
int a[50005],b[50005],tag[505];
vector<int> vec[505]; //保持各个块的值

void reset(int x){  //不完整块改变值 需要对该块进行排序保持其单调性
    vec[x].clear();
    for(int i=(x-1)*k+1;i<=min(x*k,n);++i){
        vec[x].push_back(a[i]);
    }
    sort(vec[x].begin(),vec[x].end());
}

void add(int l,int r,int c){
    for(int i=l;i<=min(r,b[l]*k);++i){ //左边的不完整块
        a[i]+=c;
    }
    reset(b[l]);
    if(b[l]!=b[r]){
        for(int i=(b[r]-1)*k+1;i<=r;++i){ //右边的不完整的块
            a[i]+=c;
        }
        reset(b[r]);
    }
    for(int i=b[l]+1;i<=b[r]-1;++i){ //中间完整的块 b[l]+1~b[r]-1
        tag[i]+=c;
    }
}

int query(int l,int r,int c){
    int res=0;
    for(int i=l;i<=min(r,b[l]*k);++i){
        if(a[i]+tag[b[l]]<c){
            ++res;
        }
    }
    if(b[l]!=b[r]){
        for(int i=(b[r]-1)*k+1;i<=r;++i){
            if(a[i]+tag[b[r]]<c){
                ++res;
            }
        }
    }
    for(int i=b[l]+1;i<=b[r]-1;++i){
        int x=c-tag[i];
        res+=lower_bound(vec[i].begin(),vec[i].end(),x)-vec[i].begin();
    }
    return res;
}   

int main(){
    n=read(),k=sqrt(n);
    for(int i=1;i<=n;++i){
        a[i]=read();
    }
    for(int i=1;i<=n;++i){
        b[i]=(i-1)/k+1;
        vec[b[i]].push_back(a[i]);
    }
    for(int i=1;i<=b[n];++i){
        sort(vec[i].begin(),vec[i].end());
    }
    for(int i=1;i<=n;++i){
        int op=read(),l=read(),r=read(),c=read();
        if(0==op){
            add(l,r,c);
        }else{
           printf("%d\n",query(l,r,c*c));
        }
    }
    return 0;
}

6277. 数列分块入门 3

题面传送门
分块思路:同题面二其实是差不多的,只是这次是找c的前驱(即小于c的最大值),利用set来维护
块的块区间的单调性。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)

#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 1000000000+7;
const int maxn = 1000;
using namespace std;

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int n,k;
int v[100010],b[100010],tag[505];
set<int> st[505];
inline void add(int l,int r,int c){
    for(int i=l;i<=min(b[l]*k,r);++i){
        st[b[l]].erase(v[i]);
        v[i]+=c;
        st[b[l]].insert(v[i]);
    }
    if(b[l]!=b[r]){
        for(int i=(b[r]-1)*k+1;i<=r;++i){
            st[b[r]].erase(v[i]);  
            v[i]+=c; 
            st[b[r]].insert(v[i]);
        }
    }
    for(int i=b[l]+1;i<=b[r]-1;++i){
        tag[i]+=c;
    }
}
inline int query(int l,int r,int c){
    int ans=-1;
    for(int i=l;i<=min(r,b[l]*k);++i){
        int x=v[i]+tag[b[l]];
        if(x<c){
            ans=max(ans,x);
        }
    }
    if(b[l]!=b[r]){
        for(int i=(b[r]-1)*k+1;i<=r;++i){
            int x=v[i]+tag[b[r]];
            if(x<c){
                ans=max(ans,x);
            }
        }
    }
    for(int i=b[l]+1;i<=b[r]-1;++i){
        int x=c-tag[i];
        set<int>::iterator it=st[i].lower_bound(x);  //找比x小的
        if(it==st[i].begin()){
            continue;
        }
        --it;
        ans=max(ans,*it+tag[i]);
    }
    return ans;
}

int main(){
    fin;
    n=read(),k=sqrt(n);
    for(int i=1;i<=n;++i){
        v[i]=read();
    }
    for(int i=1;i<=n;++i){
        b[i]=(i-1)/k+1;
        st[b[i]].insert(v[i]);
    }
    for(set<int>::iterator it=st[1].begin();it!=st[1].end();++it){
        cout<<*it<<endl;
    }
    for(int i=1;i<=n;++i){
        int opt=read(),l=read(),r=read(),c=read();
        if(0==opt){
            add(l,r,c);
        }else{
            printf("%d\n",query(l,r,c));
        }
    }
    return 0;
}

6277. 数列分块入门 4

题面传送门
分块思路:区间加法,区间求和。
tag数组标记块区间累加的c
sum数组维护该块区间的区间和,记得long long类型。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)

#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 1000000000+7;
const int maxn = 1000;
using namespace std;

inline ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int n,k;
int b[50010];
ll v[50010],tag[505];
ll sum[505];

inline void add(int l,int r,int c){
    for(int i=l;i<=min(b[l]*k,r);++i){
        v[i]+=c; //更新值
        sum[b[l]]+=c; //更新该块的区间和
    }
    if(b[l]!=b[r]){
        for(int i=(b[r]-1)*k+1;i<=r;++i){
            v[i]+=c;
            sum[b[r]]+=c;
        }
    }
    for(int i=b[l]+1;i<=b[r]-1;++i){
        tag[i]+=c;
    }
}
inline ll query(int l,int r){
    ll ans=0;
    for(int i=l;i<=min(b[l]*k,r);++i){
        ans+=v[i]+tag[b[l]];
    }
    if(b[l]!=b[r]){
        for(int i=(b[r]-1)*k+1;i<=r;++i){
            ans+=v[i]+tag[b[r]];
        }
    }
    for(int i=b[l]+1;i<=b[r]-1;++i){
        ll t=tag[i]*k;
        ans+=sum[i]+t;
    }
    return ans;
}
int main(){
    n=read(),k=sqrt(n);
    for(int i=1;i<=n;++i){
        v[i]=read();
    }
    for(int i=1;i<=n;++i){
        b[i]=(i-1)/k+1;
        sum[b[i]]+=v[i];
    }
    for(int i=1;i<=n;++i){
        int opt=read(),l=read(),r=read(),c=read();
        if(0==opt){
            add(l,r,c);
        }else{
            printf("%lld\n",query(l,r)%(c+1));
        }
    }
    return 0;
}

6277. 数列分块入门 5

题面传送门
分块思路:区间开方,区间求和。
不完整的块直接暴力求即可。
完整的块区间怎么求呢。
根据题面ans<=2^31-1,我们考虑最坏情况,即区间是150000,求该区间和,那么每个位置最大约为2^16次方,
那么我们至多每个区间开方4次(向下取整),整个区间都变成0或者1.这些区间我们标记一下,下次对该块区间的开方就可以直接跳过。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)

#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 1000000000+7;
const int maxn = 1000;
using namespace std;

inline ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int n,k;
ll sum[50005],v[50005];
int b[50005];
bool flag[305];

inline void solve(int x){
    if(flag[x]){
        return;
    }
    flag[x]=true;
    sum[x]=0;
    for(int i=(x-1)*k+1;i<=x*k;++i){
        v[i]=sqrt(v[i]);
        sum[x]+=v[i];
        if(v[i]>1){
            flag[x]=false;
        }
    }
}

inline void add(int l,int r){
    for(int i=l;i<=min(b[l]*k,r);++i){
        sum[b[l]]-=v[i];
        v[i]=sqrt(v[i]);
        sum[b[l]]+=v[i];
    }
    if(b[l]!=b[r]){
        for(int i=(b[r]-1)*k+1;i<=r;++i){
            sum[b[r]]-=v[i];
            v[i]=sqrt(v[i]);
            sum[b[r]]+=v[i];
        }
    }
    for(int i=b[l]+1;i<=b[r]-1;++i){
        solve(i);
    }
}

inline ll query(int l,int r){
    int ans=0;
    for(int i=l;i<=min(b[l]*k,r);++i){
        ans+=v[i];
    }
    if(b[l]!=b[r]){
        for(int i=(b[r]-1)*k+1;i<=r;++i){
            ans+=v[i];
        }
    }
    for(int i=b[l]+1;i<=b[r]-1;++i){
        ans+=sum[i];
    }
    return ans;
}

int main(){
    fin;
    n=read(),k=sqrt(n);
    for(int i=1;i<=n;++i){
        v[i]=read();
    }
    for(int i=1;i<=n;++i){
        b[i]=(i-1)/k+1;
        sum[b[i]]+=v[i];
    }
    for(int i=1;i<=n;++i){
        int opt=read(),l=read(),r=read(),c=read();
        if(0==opt){
            add(l,r);
        }else{
            printf("%lld\n",query(l,r));
        }
    }
}

6277. 数列分块入门 6

分块思路:mtag数组是块区间的乘法标记,atag数组是块区间的加法标记。
乘法:根据 (a+b)*c=a*c+b*c. a为乘法标记 b为加法标记
加法:直接加atag数组即可。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)

#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 10007;
const int maxn = 1000;
using namespace std;

inline ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int n,k;
int v[100005],mtag[1005],atag[1005],b[100005];
inline void reset(int x){
   for(int i=(x-1)*k+1;i<=min(x*k,n);++i){
        v[i]=(v[i]*mtag[x]+atag[x])%mod;
   }
   mtag[x]=1,atag[x]=0;
}
inline void solve(int f,int l,int r,int c){
   reset(b[l]);
   for(int i=l;i<=min(b[l]*k,r);++i){
       if(0==f)v[i]+=c;
       else v[i]*=c;
       v[i]%=mod;
   }
   if(b[l]!=b[r]){
       reset(b[r]);
       for(int i=(b[r]-1)*k+1;i<=r;++i){
           if(0==f)v[i]+=c;
           else v[i]*=c;
           v[i]%=mod;
       }
   }
   for(int i=b[l]+1;i<=b[r]-1;++i){
       if(0==f)atag[i]=(atag[i]+c)%mod;
       else{
           mtag[i]=(mtag[i]*c)%mod;
           atag[i]=(atag[i]*c)%mod;
       }
   }
}
int main(){
    n=read(),k=sqrt(n);
    for(int i=1;i<=n;++i){
        v[i]=read();
    }
    for(int i=1;i<=n;++i){
        b[i]=(i-1)/k+1;
    }
    for(int i=1;i<=b[n];++i){
        mtag[i]=1;
    }
    for(int i=1;i<=n;++i){
        int opt=read(),l=read(),r=read(),c=read();
        if(opt!=2){
            solve(opt,l,r,c);
        }else {
            printf("%d\n",(v[r]*mtag[b[r]]+atag[b[r]])%mod);
        }
    }
    return 0;
}

分块思路:题目涉及单点插入,单点查询。
那么我们就用一个可变的数组来存储块区间之间的值。
这里可以涉及重新分块的操作,虽然在这一题并不需要这样的操作,不过若是有区间查询的操作的话,
那么这个操作就比较有用了。

引用hzwer:
先说随机数据的情况
之前提到过,如果我们块内用数组以外的数据结构,能够支持其它不一样的操作,
比如此题每块内可以放一个动态的数组,每次插入时先找到位置所在的块,再暴力插入,把块内的其它元素直接向后移动一位,当然用链表也是可以的。
查询的时候类似,复杂度分析略。
但是这样做有个问题,如果数据不随机怎么办?
如果先在一个块有大量单点插入,这个块的大小会大大超过√n,那块内的暴力就没有复杂度保证了。
还需要引入一个操作:重新分块(重构)
每根号n次插入后,重新把数列平均分一下块,重构需要的复杂度为O(n),
重构的次数为√n,所以重构的复杂度没有问题,而且保证了每个块的大小相对均衡。
当然,也可以当某个块过大时重构,或者只把这个块分成两半。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<string>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#include<set>
#include<map>
#include<cstdio>
#include<limits.h>
#define MOD 1000000007
#define fir first
#define sec second
#define fin freopen("/home/ios666/Documents/input.txt", "r", stdin)
#define fout freopen("/home/ios666/Documents/output.txt", "w", stdout)
#define mes(x, m) memset(x, m, sizeof(x))
#define Pii pair<int, int>
#define Pll pair<ll, ll>
#define INF 1e9+7
#define inf 0x3f3f3f3f
#define Pi 4.0*atan(1.0)

#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-9;
const int mod = 10007;
const int maxn = 1000;
using namespace std;

inline ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int n,k,m;
int v[100005];
int st[200005];
vector<int> vec[2005];
int tol=0;

inline pair<int,int> query(int l){
    int x=1;
    while(l>vec[x].size()){
       l-=vec[x].size();
       ++x;
    }
    return make_pair(x,l-1);
}

inline void rebuild(){
    int ed=0;
    for(int i=1;i<=m;++i){
        for(vector<int>::iterator it=vec[i].begin();it!=vec[i].end();++it){
            st[++ed]=*it;
        }
        vec[i].clear();
    }
    int blo=sqrt(ed);
    for(int i=1;i<=ed;++i){
        vec[(i-1)/blo+1].push_back(st[i]);
    }
    m=(ed-1)/blo+1;
}

inline void insert(int a,int b){
    ++tol;
    Pii x=query(a);
    vec[x.first].insert(vec[x.first].begin()+x.second,b);
    if(tol==k){  //每进行sqrt(n)插入操作进行一次重新分块
        rebuild();
        tol=0;
    }
}

int main(){
    fin;
    n=read(),k=sqrt(n);
    for(int i=1;i<=n;++i){
        v[i]=read();
        vec[(i-1)/k+1].push_back(v[i]);
    }
    m=(n-1)/k+1;
    for(int i=1;i<=n;++i){
        int op=read(),l=read(),r=read(),c=read();
        if(0==op){
            insert(l,r);
        }else{
            Pii t=query(r);
            printf("%d\n",vec[t.first][t.second]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Viscu/article/details/82051839