「CF551E」GukiZ and GukiZiana【分块】

题目链接

题意

  • 就是一个数组,两种操作,第一种区间加值,第二种查询某一个数在这个数组里的最远距离,比如数组1 2 1 2查询1的话答案就是2

题解

  • 考虑分块,将每块按照值升序,值相同按照在原数组的下标升序排序,每次加值的时候给 l l l r r r所在的块内的所有原位置为 [ l , r ] [l,r] [l,r]的数加上这个值,然后将这两个块暴力 s o r t sort sort,然后整块打上标记,查询的时候对每一个块二分查询

复杂度

  • O ( n n log ⁡ n ) O(n\sqrt n\log n) O(nn logn)

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int smaxn=800;
int n,m,a[maxn],c,d,opt,x;

namespace blocking{
    int tot,belong[maxn],siz[smaxn],block;  //分块基本数据
    long long mark[maxn];                //解题数据
    pair<long long,int> v[maxn];
    void init(int n) {
        block=(int)sqrt((double)n);
        tot=n%block?(n/block+1):(n/block);
        for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
        for(int i=1;i<=tot;i++) siz[i]=(min(i*block,n)-(i-1)*block);
    }
    void add(int l,int r,long long val) {
        if(belong[r]-belong[l]<=1) {
            for(int i=(belong[l]-1)*block+1;i<=min(belong[r]*block,n);i++) {
                if(v[i].second>=l&&v[i].second<=r) v[i].first+=val;
            }
            for(int i=belong[l];i<=belong[r];i++) sort(v+(i-1)*block+1,v+min(i*block,n)+1);
            return;
        }
        for(int i=(belong[l]-1)*block+1;i<=belong[l]*block;i++) {
            if(v[i].second<=r&&v[i].second>=l) v[i].first+=val;
        }
        sort(v+(belong[l]-1)*block+1,v+min(belong[l]*block,n)+1);
        for(int i=belong[l]+1;i<=belong[r]-1;i++) mark[i]+=val;
        for(int i=(belong[r]-1)*block+1;i<=min(belong[r]*block,n);i++) {
            if(v[i].second<=r&&v[i].second>=l) v[i].first+=val;
        }
        sort(v+(belong[r]-1)*block+1,v+min(belong[r]*block,n)+1);
    }

    int query(long long x) {
        int l=-1,r=-1;
        for(int i=1;i<=tot;i++) {
            int p=lower_bound(v+(i-1)*block+1,v+min(i*block,n)+1,make_pair(x-mark[i],0))-v;
            if(p<=min(i*block,n)&&v[p].first==x-mark[i]) {l=v[p].second;break;}
        }
        for(int i=tot;i>=1;i--) {
            int p=upper_bound(v+(i-1)*block+1,v+min(i*block,n)+1,make_pair(x-mark[i],n+1))-v;
            if(p>(i-1)*block+1&&v[p-1].first==x-mark[i]) {r=v[p-1].second;break;}
        }
        if(l==-1||r==-1) return -1;
        return r-l;
    }

    void debug() {
        printf("block= %d, tot= %d\n",block,tot);
        for(int i=1;i<=n;i++) printf("%d%c",belong[i],i==n?'\n':' ');
    }
}
using namespace blocking;

int main() {
    scanf("%d %d",&n,&m);init(n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),v[i]=make_pair(a[i],i);
    for(int i=1;i<=tot;i++) sort(v+(i-1)*block+1,v+min(i*block,n)+1);
    for(int i=1;i<=m;i++) {
        scanf("%d",&opt);
        if(opt==1) {
            scanf("%d %d %d",&c,&d,&x);
            add(c,d,x);
        }else {
            scanf("%d",&x);
            printf("%d\n",query(x));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值