• 【题解】LibreOJ #6279. 数列分块入门 3


    题目链接:LibreOJ #6279. 数列分块入门 3

    题面

    题目描述

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

    输入格式

    第一行输入一个数字 (n)

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

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

    ( m opt=0),表示将位于 (left[l, r ight]) 的之间的数字都加 (c)

    ( m opt=1),表示询问 (left[l, r ight])(c) 的前驱的值(不存在则输出 ( m -1))。

    输出格式

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

    样例(很水)

    Input

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

    Output

    3
    -1
    

    数据范围与提示

    对于 (100\%) 的数据,(1leq nleq 100000,-2^{31}leq { m others})({ m ans}leq 2^{31}-1)

    对题目的吐槽

    这个样例是真的水,从样例中根本找不出自己程序任何错误。。。。

    我一开始程序内层循环用的是i变量,样例居然给我过了?!

    思路

    审题

    这道题他说要求前驱的值。要求前驱的值如果直接暴力查找的话肯定会T飞,所以需要用到分块来优化。

    解法

    加c的核心代码如下:

    for (int j = x; j <= r[b[x]]; j++) a[j] += k;         //左边不完整块暴力相加
    for (int j = l[b[x]]; j <= r[b[x]]; j++) d[j] = a[j]; //将a[]数组复制一份给d[]数组,以此来达到将数组排序而不破坏原数组顺序
    sort(d + l[b[x]], d + r[b[x]] + 1);                   //将d[]数组排序
    for (int j = l[b[y]]; j <= y; j++) a[j] += k;         //右边不完整块暴力相加
    for (int j = l[b[y]]; j <= r[b[y]]; j++) d[j] = a[j]; //将a[]数组复制一份给d[]数组,以此来达到将数组排序而不破坏原数组顺序
    sort(d + l[b[y]], d + r[b[y]] + 1);                    //将d[]数组排序
    for (int j = b[x] + 1; j <= b[y] - 1; j++)  lazy[j] += k; //用lazyp[]数组对中间完整块做标记,减少运算量
    

    查询的核心代码如下:

    for (int j = x; j <= r[b[x]]; j++) //暴力查询左边不完整块
      if ((lazy[b[x]] + a[j] < k) && (lazy[b[x]] + a[j] > maxx)/*需要保证这个比k小并且比maxx大*/)
        maxx = lazy[b[x]] + a[j]; 
    for (int j = l[b[y]]; j <= y; j++) //暴力查询右边不完整块
      if ((lazy[b[y]] + a[j] < k) && (lazy[b[y]] + a[j] > maxx))
        maxx = lazy[b[y]] + a[j];
    for (int j = b[x] + 1; j <= b[y] - 1; j++) {
      if (k - lazy[j] <= d[(j - 1) * block + 1]) continue;
      int num = lower_bound(d + l[j], d + r[j] + 1, k - lazy[j]) - d - 1; 
      //lower_bound函数返回的是大于或等于k - lazy[j]的第一个数的地址(指针),减去d可得数组下标,再减一即可得到前驱
      maxx = maxx > (d[num] + lazy[j]) ? maxx : (d[num] + lazy[j]);
    }
    if (maxx == -1) write(-1); //如果maxx没变(即没找到),则输出-1
    else write(maxx); //否则输出maxx(前驱)
    

    代码

    这次的代码是LibreOJ格式化过的,总觉得有亿点奇怪

    #include <iostream>
    #include <algorithm> //sort()要用
    #include <cstdio>
    #include <cmath> //sqrt()y要用
    #define int long long
    using namespace std;
    
    int a[1000005], d[1000005], l[1005], r[1005], b[1000005], lazy[1005];
    int n, q, block, tot, x, y, k;
    int c;
    
    inline int read() { //快读
        int X = 0;
        bool flag = 1;
        char ch = getchar();
    
        while (ch < '0' || ch > '9') {
            if (ch == '-')
                flag = 0;
    
            ch = getchar();
        }
    
        while (ch >= '0' && ch <= '9') {
            X = (X << 1) + (X << 3) + ch - '0';
            ch = getchar();
        }
    
        if (flag)
            return X;
    
        return ~(X - 1);
    }
    
    inline void write(int X) { //快写
        if (X < 0) {
            putchar('-');
            X = ~(X - 1);
        }
    
        int s[50], top = 0;
    
        while (X) {
            s[++top] = X % 10;
            X /= 10;
        }
    
        if (!top)
            s[++top] = 0;
    
        while (top)
            putchar(s[top--] + '0');
    
        putchar('
    ');
        return;
    }
    
    
    signed main() {
        n = read();
    
        for (int i = 1; i <= n; i++)
            a[i] = read();
    
        block = sqrt(n), tot = n / block;
    
        if (n % block)
            tot++; //如果数的个数不是块长的倍数的话,还要再增加一个块的个数
    
        for (int i = 1; i <= n; i++)
            b[i] = (i - 1) / block + 1, d[i] = a[i];
    
        for (int i = 1; i <= tot; i++)
            l[i] = (i - 1) * block + 1, r[i] = i * block;
    
        r[tot] = n;
    
        for (int i = 1; i <= tot; i++)
            sort(d + l[i], d + r[i] + 1);
    
        for (int i = 1; i <= n; i++) {
            c = read();
            x = read();
            y = read();
            k = read();
    
            if (c == 0) {
                if (b[x] == b[y]) { //如果x和y在同一个块内,就直接只能暴力相加
                    for (int j = x; j <= y; j++)
                        a[j] += k;
    
                    for (int j = l[b[x]]; j <= r[b[x]]; j++)
                        d[j] = a[j];
    
                    sort(d + l[b[x]], d + r[b[x]] + 1);
                } else {
                    for (int j = x; j <= r[b[x]]; j++) //左边不完整块暴力相加
                        a[j] += k;
    
                    for (int j = l[b[x]]; j <= r[b[x]]; j++) //将a[]数组复制一份给d[]数组,以此来达到将数组排序而不破坏原数组顺序
                        d[j] = a[j];
    
                    sort(d + l[b[x]], d + r[b[x]] + 1); //将d[]数组排序
    
                    for (int j = l[b[y]]; j <= y; j++) //右边不完整块暴力相加
                        a[j] += k;
    
                    for (int j = l[b[y]]; j <= r[b[y]]; j++) //将a[]数组复制一份给d[]数组,以此来达到将数组排序而不破坏原数组顺序
                        d[j] = a[j];
    
                    sort(d + l[b[y]], d + r[b[y]] + 1); //将d[]数组排序
    
                    for (int j = b[x] + 1; j <= b[y] - 1; j++) //用lazyp[]数组对中间完整块做标记,减少运算量
                        lazy[j] += k;
                }
            }
    
            if (c == 1) {
                int maxx = -1;
    
                if (b[x] == b[y]) { //如果x和y在同一个块内,就直接只能暴力求解
                    for (int j = x; j <= y; j++)
                        if ((lazy[b[x]] + a[j] < k) && (lazy[b[x]] + a[j] > maxx))
                            maxx = lazy[b[x]] + a[j];
    
                    if (maxx == -1)
                        write(-1);
                    else
                        write(maxx);
                    continue;
                } else {
                    for (int j = x; j <= r[b[x]]; j++) //暴力查询左边不完整块
                        if ((lazy[b[x]] + a[j] < k) && (lazy[b[x]] + a[j] > maxx))
                            maxx = lazy[b[x]] + a[j];
                    for (int j = l[b[y]]; j <= y; j++) //暴力查询右边不完整块
                        if ((lazy[b[y]] + a[j] < k) && (lazy[b[y]] + a[j] > maxx))
                            maxx = lazy[b[y]] + a[j];
                    for (int j = b[x] + 1; j <= b[y] - 1; j++) {
                        if (k - lazy[j] <= d[(j - 1) * block + 1])
                            continue;
                        int num = lower_bound(d + l[j], d + r[j] + 1, k - lazy[j]) - d - 1;
                        //lower_bound函数返回的是大于或等于k - lazy[j]的第一个数的地址(指针),减去d可得数组下标,再减一即可得到前驱
                        maxx = (maxx > (d[num] + lazy[j])) ? maxx : (d[num] + lazy[j]);
                    }
                    if (maxx == -1) //如果maxx没变(即没找到),则输出-1
                        write(-1);
                    else
                        write(maxx); //否则输出maxx(前驱)
                }
            }
        }
    
        return 0;
    }
  • 相关阅读:
    让美国震惊的10大营销案例
    嵌入式培训为什么选凌阳教育?
    推荐几本互联网行业的经典书目
    谈谈被误解的友情链接交换条件【转】
    20 tips for building modern sites while supporting old versions of IE
    国外PHP大师给初学者的8条建议
    专访许雪松:深入理解嵌入式开发
    周宁:做一个生意之前,请自问自己6个问题
    《时代》百大影响力人物:任正非李开复上榜
    TPL DataFlow初探(二)
  • 原文地址:https://www.cnblogs.com/g-mph/p/14619660.html
Copyright © 2020-2023  润新知