【分块】三

开头日常感谢hzw学长

终于到达本组题了呼

本组成员:老司机hgz,可爱的lln同学and帅气可爱天真无邪的我。

题面(咳咳咳才不是为了黑ty)

题目描述

题目背景:

国民男神ty又遇到了一个小难题,他在和xqj大神的争论中(谁更强),ty表示自己不会这个问题(装弱),于是他将这个问题交给了身为ty小迷弟(妹)的你。

题目描述:

给一个长为n的数列,以及n次操作。每次操作均有一个操作3个数字组成(operate,l,r,x操作共有两种

0l~r区间每个数加上x

1询问l~r区间内小于某个值x的前驱(比其小的最大元素),若不存在则输出-1

输入

第一行:一个整数n,表示数列长为n,以及有n次操作。

第二行:n个数,表示原来的数列。

3~n+2行:每行四个数:operate,l,r,x,分别表示操作的类型、区间l~r,和加入的元素x

输出

n行,每行一个数,输出lr区间内小于某个值x的前驱(比其小的最大元素)。

样例输入

3

1 2 3

1 1 2 2

0 1 2 1

1 1 2 2

 

样例输出

1
-1

提示

对于50%的数据 n<=1000

对于100%的数据 n<=30000

保证运算过程中数据不超过64位整数

保证数据全部随机

【格式什么的就不要介意了吧嘤嘤嘤】

【分析】

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

与第二道题目其实没有本质上的区别。但这里还是进行一下解释(就当前面几道没有做过)。

本题运用分块来做可以提高解题效率。

令每一个块的大小为m,把整个序列分成n/m块。我们可以知道,当mn/m尽量平衡的时候,程序的效率是最高的,所以我们把这个数列分成√n块。

预备:有若干个数组abblocktaga用来处理一个序列中非完整块的数的状态,b则用来处理一个序列中完整块的数的状态,block指每个数所在的块的序,tag用来储存每个完整块的增加量(tag的使用在后面修改状态下会进行阐述)

我们先来考虑查询的状态(可以理解为本道题是为了查询而修改的),已知lr,首先l~r中完整的块可以直接进行二分查找每个块找出符合题意的一个数,这个查询在b数组中进行(可以直接对查询的x-tag[i]方便处理),非完整的块的数在a数组中暴力进行。所有的数在查询过程的依次比较得出符合题目要求的最大值。

然后是考虑修改状态,仍旧是l~r的区间中,完整的块我们可以直接在此区间的tag[i]中加上x,非完整的块我们对此在a数组中暴力加法,然后对其修改的值在b数组中排序叠加,这样即可为下次查询服务。

其他细节可以看ac代码。

ac代码

#include <map>
#include <set>
#include <cmath>
#include <stack>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define mod 998244353
#define pi acos(-1)
#define inf 0x7fffffff
#define ll long long
using namespace std;
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;
}
ll n, blo;
ll v[50005], bl[50005], atag[50005];
vector<ll> ve[505];
void reset(ll x)
{
    ve[x].clear();
    for (ll i = (x - 1) * blo + 1; i <= min(x * blo, n); i++)
        ve[x].push_back(v[i]);
    sort(ve[x].begin(), ve[x].end());
}
void add(ll a, ll b, ll c)
{
    for (ll i = a; i <= min(bl[a] * blo, b); i++)
        v[i] += c;
    reset(bl[a]);
    if (bl[a] != bl[b])
    {
        for (ll i = (bl[b] - 1) * blo + 1; i <= b; i++)
            v[i] += c;
        reset(bl[b]);
    }
    for (ll i = bl[a] + 1; i <= bl[b] - 1; i++)
        atag[i] += c;
}
ll query(ll a, ll b, ll c)
{
    ll ans = -1;
    for (ll i = a; i <= min(bl[a] * blo, b); i++)
        if (v[i] + atag[bl[a]] < c)
            ans = max(ans, atag[bl[a]] + v[i]);
    if (bl[a] != bl[b])
        for (ll i = (bl[b] - 1) * blo + 1; i <= b; i++)
            if (v[i] + atag[bl[b]] < c)
                ans = max(ans, v[i] + atag[bl[b]]);
    for (ll i = bl[a] + 1; i <= bl[b] - 1; i++)
    {
        ll x = c - atag[i];
        vector<ll>::iterator it1 = lower_bound(ve[i].begin(), ve[i].end(), x);
        if (it1 != ve[i].begin())
        {
            it1--;
            ans = max(ans, *it1 + atag[i]);
        }
    }
    return ans;
}
int main()
{
    freopen("lln.in", "r", stdin);
    freopen("lln.out", "w", stdout);
    n = read();
    blo = sqrt(n);
    for (ll i = 1; i <= n; i++)
        v[i] = read();
    for (ll i = 1; i <= n; i++)
    {
        bl[i] = (i - 1) / blo + 1;
        ve[bl[i]].push_back(v[i]);
    }
    for (ll i = 1; i <= bl[n]; i++)
        sort(ve[i].begin(), ve[i].end());
    for (ll i = 1; i <= n; i++)
    {
        ll f = read(), a = read(), b = read(), c = read();
        if (f == 0)
            add(a, b, c);
        if (f == 1)
            printf("%lld
", query(a, b, c));
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

(草率了事的我先逃一步)

原文地址:https://www.cnblogs.com/Hathawaxy/p/6894876.html