#6279. 数列分块入门 3(区间修改,查询权值前驱)

#6279. 数列分块入门 3

在这里插入图片描述
在这里插入图片描述
这是使用hzwer建议的set写的分块代码,set自动排序,支持二分查找,但是常数较大,比我下面写的用vector实现的分块慢了三倍,空间大了10倍。

#include <cstdio>
#include <cstring>
#include <cctype>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
using namespace std;
typedef int ll;

const int N = 500007, M = 500007, INF = 0x3f3f3f3f;

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;
}

//就是要避免重复的权值的,所以用set

int n, m;
int v[N];
int block, bi[N];
int add_tag[N];
set<int>st[N];

void modify(int a, int b, int c)
{
    for(int i = a; i <= min(bi[a] * block, b); ++ i){
        st[bi[a]].erase(v[i]);
        v[i] += c;
        st[bi[a]].insert(v[i]);
    }
    if(bi[a] != bi[b]){
        for(int i = (bi[b] - 1) * block + 1; i <= b; ++ i){
            st[bi[b]].erase(v[i]);
            v[i] += c;
            st[bi[b]].insert(v[i]);
        }
    }
    for(int i = bi[a] + 1; i <= bi[b] - 1; ++ i){
        add_tag[i] += c;
    }
}

int query(int a, int b, int c)
{
    int ans = -1;
    for(int i = a; i <= min(bi[a] * block, b); ++ i){
        int val = v[i] + add_tag[bi[a]];
        if(val < c)ans = max(ans, val);
    }
    if(bi[a] != bi[b]){
        for(int i = (bi[b] - 1) * block + 1; i <= b; ++ i){
            int val = v[i] + add_tag[bi[b]];
            if(val < c)ans = max(ans, val);
        }
    }
    for(int i = bi[a] + 1; i <= bi[b] - 1; ++ i){
        int val = c - add_tag[i];
        set<int> :: iterator it = st[i].lower_bound(val);
        if(it == st[i].begin())continue;
        -- it;
        ans = max(ans, *it + add_tag[i]);
    }
    return ans;
}

int main()
{
    n = read();
    block = sqrt(n);
    for(int i = 1; i <= n; ++ i)
        v[i] = read();

    for(int i = 1; i <= n; ++ i){
        bi[i] = (i - 1) / block + 1;
        st[bi[i]].insert(v[i]);
    }
            //cout << "ok" << endl;
    for(int i = 1; i <= n; ++i){
        int op = read(), a = read(), b = read(), c = read();
        if(op == 0){
            modify(a, b, c);
        }
        else if(op == 1){
            printf("%d\n", query(a, b, c));
        }
    }
    return 0;
}

vector实现的代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#define x first
#define y second
#define debug(x) cout << x << "ok" << endl
using namespace std;
typedef long long ll;
//typedef __int128 int;oj上可用,编译器上不可用
const int N = 200007, M = 507, INF = 0x3f3f3f3f;
const double eps = 1e-8;
typedef pair<int, int>PII;

int n, m;
int a[N];
int block, cnt;
vector<int>vc[M];
int belong[N];
int add_lz[M];

void pre()//块间无序,块内有序
{
    for(int i = 1; i <= cnt; ++ i)
        sort(vc[i].begin(), vc[i].end());
}

void update(int block_num)//sqrt(n)
{
    vc[block_num].clear();
    //上一个块的个数+1到这个块的个数——遍历该块的所有元素
    for(int i = (block_num - 1) * block + 1; i <= block_num * block; ++ i)
        vc[block_num].push_back(a[i]);
    sort(vc[block_num].begin(), vc[block_num].end());
}

void modify(int l, int r, int k)
{
    //先处理 l 块
    for(int i = l; i <= min(belong[l] * block, r); ++ i){
        a[i] += k;
    }
    update(belong[l]);
    if(belong[l] != belong[r]){//不是同一个块,处理 r 块
        for(int i = (belong[r] - 1) * block + 1; i <= r; ++ i){
            a[i] += k;//先只遍历r所属的块,中间的打标记
        }
        update(belong[r]);
    }
    for(int i = belong[l] + 1; i < belong[r]; ++ i){
        add_lz[i] += k;
    }
}

int query(int l, int r, int maxv)//找小于maxv的最大数(前驱)
{
    int ans = -INF;
    for(int i = l; i <= min(belong[l] * block, r); ++ i){
        if(a[i] + add_lz[belong[i]] < maxv){
            ans = max(ans, a[i] + add_lz[belong[i]]);
        }
    }
    if(belong[l] != belong[r]){
        for(int i = (belong[r] - 1) * block + 1; i <= r; ++ i){
            if(a[i] + add_lz[belong[i]] < maxv){
                ans = max(ans, a[i] + add_lz[belong[i]]);
            }
        }
    }
    for(int i = belong[l] + 1; i < belong[r]; ++ i){
        if(vc[i].at(0) + add_lz[i] >= maxv)//块内最小值+懒惰标记都已经大于maxv那就没有继续的必要
            continue;
        int k = lower_bound(vc[i].begin(), vc[i].end(), maxv - add_lz[i]) - vc[i].begin();
        ans = max(ans, vc[i].at(k - 1) + add_lz[i]);
    }
    return ans;
}

int main()
{
    scanf("%d", &n);
    block = sqrt(n);//每个块的个数
    for(int i = 1; i <= n; ++ i){
        scanf("%d", &a[i]);
        belong[i] = (i - 1) / block + 1;//从1开始,求当前是第几块
        vc[belong[i]].push_back(a[i]);
        if(i % block == 1)//注意有头块和尾块,因为n不一定是完全平方数
            cnt ++ ;
    }
    pre();//排序预处理
    for(int i = 1; i <= n; ++ i){
        int op, l, r, c;
        scanf("%d%d%d%d", &op, &l, &r, &c);
        if(op == 0){
            modify(l, r, c);
        }
        else {
            int res = query(l, r, c);//找前驱
            if(res == -INF){
                puts("-1");
                continue;
            }
            printf("%d\n", res);
        }
    }
    return 0;
}


已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页