E. GukiZ and GukiZiana【分块(块内排序)

文章讲述了如何使用根号分块和预处理排序来解决一个给定大量数和区间询问的问题。操作包括区间加一个数和查询区间内某个数的最大坐标差。通过维护块内的有序性并重置部分块,以及在查询时使用两次二分查找,可以高效地完成任务。作者提到分块技术在处理这类问题时具有广泛的应用性。
摘要由CSDN通过智能技术生成

题意:给你5e4个数,1e6个询问(时限10s),两个操作:操作一区间加一个数,操作二,区间询问数y的最大坐标差,区间没有y就输出 -1;

分析:根号分块,预处理排序,块内维护排序,reset函数就是当区间加作用在不完整块的时候,用来重新排序,维护这个块的有序性,最后查询的时候,两次二分,分别二分最小最大位置,输出R- L即可;

不过第一版代码的询问不知道为啥错了,wa32,一直想不明白

PS:今天终于学了分块,感觉数据结构现在才算把大的漏洞补完,个人感觉分块还是很厉害的,毕竟通用面更广;

 AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 15;
const int mo = 998244353;
#define pb push_back
#define pii pair<int,int>
#define ft first
#define sd second
#define ffor(i,a,b,c) for(int i=(a);i<(b);i+=(c))
#define For(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
#define rfor(i,a,b,c) for(int i=(a);i>(b);i-=(c))
#define Rfor(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
#define all(x) (x).begin(), (x).end()
#define debug1(x) cerr<<"! "<<x<<endl;
#define debug2(x,y) cerr<<"#  "<<x<<" "<<y<<endl;
#define int long long
class Partition
{
public:
Partition(int _n): n(_n) {
    bl.resize(n + 1), v.resize(n + 1), tag.resize(n + 1);
    ve.resize(n + 1);
    blo = sqrt(n);
    for (int i = 1; i <= n; i++) {
        cin >> v[i];
        bl[i] = (i - 1) / blo + 1;
        ve[bl[i]].pb({v[i], i});
    }
    for (int i = 1; i <= (n + blo - 1) / blo; i++) {
        sort(ve[i].begin(), ve[i].end());
    }
}

void reset(int x) {
    ve[bl[x]].clear();
    for (int i = (bl[x] - 1) * blo + 1; i <= min(bl[x]*blo, n); i++) {
        ve[bl[x]].pb({v[i], i});
    }
    sort(ve[bl[x]].begin(), ve[bl[x]].end());
}

void add(int l, int r, int c) {
    for (int i = l; i <= min(bl[l]*blo, r); i++) {
        v[i] += c;
    }
    reset(l);
    if (bl[l] != bl[r]) {
        for (int i = (bl[r] - 1) * blo + 1; i <= r; i++) {
            v[i] += c;
        }
        reset(r);
    }
    for (int i = bl[l] + 1; i <= bl[r] - 1; i++) {
        tag[i] += c;
    }
}

int query(int y) {
    int L = 1e9, R = -1e9;
    for (int i = 1; i <= (n + blo - 1) / blo; i++) {
        int ll = lower_bound(ve[i].begin(), ve[i].end(), make_pair(1ll*(y - tag[i]), 0ll)) - ve[i].begin();
        int rr = lower_bound(ve[i].begin(), ve[i].end(), make_pair(1ll*(y - tag[i] + 1), 0ll)) - ve[i].begin() - 1;
        if(ll <= rr) {
            L = min(L, ve[i][ll].sd);
            R = max(R, ve[i][rr].sd);
        }
    }
    // debug2(L, R);

    if (L > R) return -1;
    else return R - L;
}

private:
vector<int> bl, v, tag;
vector<vector<pii>> ve;
int blo, n;
};
void slv() {
    int n, m; cin >> n >> m;
    Partition Pt(n);
    for (int i = 1; i <= m; i++) {
        int op; cin >> op;
        if (op == 1) {
            int l, r, x; cin >> l >> r >> x;
            Pt.add(l, r, x);
        } else {
            int y; cin >> y;
            cout << Pt.query(y) << '\n';
        }
    }
}
signed main() {
    ios::sync_with_stdio(false); cin.tie(0);
    slv();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值