分块(真的是优雅的暴力)

M - 一道普通题1

 

给出一个长为 n​ 的数列,以及 n​个操作,操作涉及区间加法,询问区间内小于某个值 x​的前驱(比其小的最大元素)。

Input

第一行输入一个数字 n,1≤n≤100000。

第二行输入 n个非负整数,第 ii个数字为 ai(0≤ai≤10^9)以空格隔开。

接下来输入n行询问,每行输入四个数字 opt、l、r、c(c>0),以空格隔开。

若 opt=0,表示将位于[l,r]的之间的数字都加 c。

若 opt=1,表示询问[l,r]中 c 的前驱的值(不存在则输出 −1)。

Output

对于每次询问,输出一行一个数字表示答案。

保证所有数据在int范围内

Sample Input

4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4

Sample Output

3
-1

Hint

最初一看到这个题的这种格式就准备用树状数组来做的,可是做到询问前驱的时候就发现做不好了,然后就瞄了一下了学长的题解,才发现要用分块做,分块呢我只知道它的模板,~~~(我真的好菜哎^^),然后我就套模板一写,可提交就T了,中间又错了好多次,最后又看了看学长的代码,学长是用的vector来存每一块的数,左右两端残块暴力处理,中间的整块(如果有的话)一块块的处理,这时更新的时候就没有对原数组直接+val,而是维护一个lazy数组,表示每一块要加的数是多少,比较的时候用val-lazy[i]与原数组比较,如果有比它小的最大的数则存在前缀,且为找到的原数组的那个数+lazy[i],否则就没有。。。。。。。最后明白这才叫分块嘛,中间一块一块的处理,像我最初中间还是一个一个点处理,那分块意义何在。。。。。。。

AC代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
using namespace std;
const int maxn=100001;
int belong[maxn],l[maxn],r[maxn],b[maxn];
int lazy[maxn];
int block,num;
int n;
vector<int>s[1010];
void build()
{
    block=(int)sqrt(n);
    num=n/block;
    if(n%block) num++;
    for(int i=1;i<=num;i++)
        l[i]=(i-1)*block+1,r[i]=i*block;
    r[num]=n;//
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
        belong[i]=(i-1)/block+1;
        s[belong[i]].push_back(b[i]);
    }
    for(int i=1;i<=belong[n];i++) sort(s[i].begin(),s[i].end());
}
void update(int ll,int rr,int val)
{
    int first=belong[ll];
    int last=belong[rr];
    for(int i=ll;i<=min(belong[ll]*block,rr);i++)
        b[i]+=val;
    s[belong[ll]].clear();
    for(int i=(belong[ll]-1)*block+1;i<=min(belong[ll]*block,n);i++)
        s[belong[ll]].push_back(b[i]);
    sort(s[belong[ll]].begin(),s[belong[ll]].end());//左边残块

    if(belong[ll]==belong[rr]) return;//如果只有一个残块修改完毕

    for(int i=(belong[rr]-1)*block;i<=rr;i++)
        b[i]+=val;
    s[belong[rr]].clear();
    for(int i=(belong[rr]-1)*block+1;i<=min(belong[rr]*block,n);i++)
        s[belong[rr]].push_back(b[i]);
    sort(s[belong[rr]].begin(),s[belong[rr]].end());//右边残块

    for(int i=first+1;i<=last-1;i++)
        lazy[i]+=val;//中间的整块用lazy维护而不对原数组直接+
}
int query(int ll,int rr,int val)
{
    int first=belong[ll];
    int last=belong[rr];
    int _max=-1;
    for(int i=ll;i<=min(r[belong[ll]],rr);i++)
        if(b[i]+lazy[belong[i]]<val)
            _max=max(_max,b[i]+lazy[belong[i]]);
    if(first!=last)
    {
        for(int i=l[belong[rr]];i<=rr;i++)
            if(b[i]+lazy[belong[i]]<val)
              _max=max(b[i]+lazy[belong[i]],_max);
    }
    for(int i=first+1;i<=last-1;i++)
    {
        auto it=lower_bound(s[i].begin(),s[i].end(),val-lazy[i]);
        if(it==s[i].begin()) continue;
        --it;
        _max=max(_max,*it+lazy[i]);
    }
    return _max;
}
int main()
{
    //ios::sync_with_stdio(0),cin.tie(0);
    while(~scanf("%d",&n)){
    build();
    int m=n;
    int op,x,y,k;
    while(m--)
    {
        scanf("%d%d%d%d",&op,&x,&y,&k);
        if(op==0)
        {
            update(x,y,k);
        }
        else if(op==1)
        {
            printf("%d\n",query(x,y,k));
        }
    }
    }
    return 0;
}

例题2: poj3468  链接:http://poj.org/problem?id=3468

板子更新一下

 Ac code:

#include<stack>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
int belong[maxn],L[maxn],R[maxn];
ll a[maxn],sum[maxn],add[maxn];
void update(int l,int r,ll val)
{
    int p=belong[l],q=belong[r];
    if(p==q)
    {
        for(int i=l; i<=r; i++)
            a[i]+=val;
        sum[p]+=val*(r-l+1);
    }
    else
    {
        for(int i=p+1; i<=q-1; i++)
            add[i]+=val;
        for(int i=l; i<=R[p]; i++)
            a[i]+=val;
        sum[p]+=val*(R[p]-l+1);
        for(int i=L[q]; i<=r; i++)
            a[i]+=val;
        sum[q]+=val*(r-L[q]+1);
    }
}///对于残块一定要注意加上增量
ll getsum(int l,int r)
{
    int p=belong[l],q=belong[r];
    ll ans=0;
    if(p==q)///同一个块内用朴素方法
    {
        for(int i=l; i<=r; i++)
            ans+=a[i];
        ans+=add[p]*(r-l+1);
    }
    else
    {
        for(int i=p+1; i<=q-1; i++)
            ans+=sum[i]+add[i]*(R[i]-L[i]+1);
        for(int i=l; i<=R[p]; i++)
            ans+=a[i];///加上第p个残块的原本的值
        ans+=add[p]*(R[p]-l+1);///加上第p个残块的增量
        for(int i=L[q]; i<=r; i++)
            ans+=a[i];
        ans+=add[q]*(r-L[q]+1);
    }
    return ans;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
    int block=sqrt(n);
    for(int i=1; i<=block; i++)
    {
        L[i]=(i-1)*block+1;
        R[i]=i*block;
    }
    if(R[block]<n) block++,L[block]=R[block-1]+1,R[block]=n;
    for(int i=1; i<=block; i++)
        for(int j=L[i]; j<=R[i]; j++)
        {
            belong[j]=i;
            sum[i]+=a[j];
        }
    ll val;
    int b,c;
    char op[5];
    while(m--)
    {
        scanf("%s%d%d",op,&b,&c);
        if(op[0]=='Q') printf("%lld\n",getsum(b,c));
        else
        {
            scanf("%lld",&val);
            update(b,c,val);
        }
    }
    return 0;
}

 

最后贴个模板:

分块模板:
belong[]数组表示第i个点属于哪个块,l[],r[]分别表示第i个点所在的左右边界
block表示每块的大小,num表示有多少块
void build()
{
    block=(int)sqrt(n);
    num=n/block;
    if(n%block) num++;
    for(int i=1;i<=num;i++)
        l[i]=(i-1)*block+1,r[i]=i*block;
    r[num]=n;//最后一块的边界一定是n
    for(int i=1;i<=n;i++){
        cin>>a[i];
        belong[i]=(i-1)/block+1;
    }
    //下面维护需要的数组.....
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值