#6279. 数列分块入门 3

#6279. 数列分块入门 3

内存限制:256 MiB时间限制:1500 ms标准输入输出

题目描述

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

输入格式

第一行输入一个数字 n。

第二行输入 n 个数字,第 i 个数字为 ai,以空格隔开。

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

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

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

输出格式

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

样例输入

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

样例输出

3
-1

思路:对于不完整的块我们直接暴力枚举,用一个sum数组来维护完整区间内的加法标记,为了快速找到区间我们可以用set容器或者vector容器,我选择set,它可以自动从小到大排序,更新操作比vector简单,每次更新的时候直接erase原来的值,然后加入增加以后的值,然后对于每次查询,不在完整区间内的,直接暴力查询,在完整区间内的块就通过set自带的二分函数来实现快速查询。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5+7;
int belong[maxn],l[maxn],r[maxn];
int n,num,block;
int a[maxn],sum[maxn];
set<int>s[2005];

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

void build()
{
    block = 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++)
    {
         belong[i] = (i - 1) / block + 1;
         s[belong[i]].insert(a[i]);
    }
}

void update(int x,int y,int c)
{
    int t1 = belong[x], t2 = belong[y];

    for(int i = x; i <= min(r[t1], y); i++)
    {
        s[t1].erase(a[i]);
        a[i] += c;
        s[t1].insert(a[i]);
    }
     if(t1 != t2) for(int i = l[t2]; i <= y; i++)
    {
        s[t2].erase(a[i]);
        a[i]+=c;
        s[t2].insert(a[i]);
    }
    for(int i = t1 + 1; i < t2; i++)
        sum[i] += c;
}

int query(int x,int y,int c)
{
    int ans = -1;
    int t1 = belong[x], t2 = belong[y];
    //注意sum是加法标记
   for(int i = x; i <= min(r[t1], y); i++)
        if(a[i] + sum[t1] < c) ans = max(ans,a[i]+sum[t1]);

   if(t1 != t2) for(int i = l[t2]; i <= y; i++)
        if(a[i] + sum[t2] < c) ans = max(ans,a[i]+sum[t2]);

    for(int i = t1 + 1; i < t2; i++)
    {
        int x = c - sum[i];
        set<int>::iterator it = s[i].lower_bound(x);
        if(it == s[i].begin()) continue;
        it--;
        ans = max(ans,*it+sum[i]);
    }
    return ans;
}

int main()
{
    n = read();
    for(int i = 1; i <= n; i++)
        scanf("%d",&a[i]);
    build();
    for(int i = 1; i <= n; i++)
    {
        int op = read(), l = read(), r = read(), c = read();
        if(op == 1) printf("%d\n",query(l,r,c));
        else update(l,r,c);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sugarbliss/article/details/81223261
今日推荐