分块3-----#6279. 数列分块入门 3

题目链接:https://loj.ac/problem/6279

题目描述

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

输入格式

第一行输入一个数字 nn。

第二行输入 nn 个数字,第 ii 个数字为 a_iai​,以空格隔开。

接下来输入 nn 行询问,每行输入四个数字 \mathrm{opt}opt、ll、rr、cc,以空格隔开。

若 \mathrm{opt} = 0opt=0,表示将位于 [l, r][l,r] 的之间的数字都加 cc。

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

输出格式

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

样例

样例输入

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

样例输出 

3
-1

思路:第三题和第二题思路是样的,因为是要找c的前驱,暴力找左右不完整块的比c小最大值,再二分找完整块比c小最大值,取其中最大即可。 

ac代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int s[maxn],flag[maxn],pos[maxn],ans,tag,n,m;
vector<int>v[1010];

void reset(int x)//用于对不完整块排序 
{
    v[pos[x]].clear();
    for(int i=(pos[x]-1)*m+1;i<=min(pos[x]*m,n);i++)
        v[pos[x]].push_back(s[i]);
    sort(v[pos[x]].begin(),v[pos[x]].end());
} 

void update(int l,int r,int c)//更新值,第一个操作 
{
    for(int i=l;i<=min(pos[l]*m,r);i++)//暴力加 
        s[i]+=c;
    reset(l);
    if(pos[l]!=pos[r])
    {
        for(int i=(pos[r]-1)*m+1;i<=r;i++)//暴力加 
            s[i]+=c;
        reset(r);
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)//b数组储存完整块的加值 
    flag[i]+=c;
}

void query(int l,int r,int x)
{
    tag=0;//标记是否找到比c小的值 
    ans=-inf;
    for(int i=l;i<=min(pos[l]*m,r);i++)//暴力找左边不完整块 
    {
        if(s[i]+flag[pos[l]]<x)
        {
            ans=max(s[i]+flag[pos[l]],ans);
            tag=1;    
        } 
    }
    if(pos[l]!=pos[r])//暴力找右边不完整块 
    {
        for(int i=(pos[r]-1)*m+1;i<=r;i++)
        {
            if(s[i]+flag[pos[r]]<x)
            {
                ans=max(ans,s[i]+flag[pos[r]]);
                tag=1;
            }
        }
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++)//整块用二分查找 
    {
        int c=x-flag[i];
        int k=lower_bound(v[i].begin(),v[i].end(),c)-v[i].begin();
        if(k!=0&&v[i][k-1]+flag[i]<x)//不要忘了加回整块该加的值 
        {
            tag=1;
            ans=max(ans,v[i][k-1]+flag[i]);
        }
    }
    if(tag)
        printf("%d\n",ans);
    else
        printf("-1\n");
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&s[i]);
    m=sqrt(n);//块的大小 
    for(int i=1;i<=n;i++)
        pos[i]=(i-1)/m+1,v[pos[i]].push_back(s[i]);//每个数块的序号并储存在vector 
    for(int i=1;i<=pos[n];i++)
        sort(v[i].begin(),v[i].end());//整块排序 
    int opt,l,r,c;
    for(int i=0;i<n;i++)
    {
        scanf("%d%d%d%d",&opt,&l,&r,&c);
        if(opt==0)
            update(l,r,c);
        else
            query(l,r,c);        
    } 
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41515833/article/details/86613485
今日推荐