• M


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

    Input

    第一行输入一个数字 n,1≤n≤100000n,1≤n≤100000。

    第二行输入 nn 个非负整数,第 ii个数字为 ai(0≤ai≤109)ai(0≤ai≤109)以空格隔开。

    接下来输入nn 行询问,每行输入四个数字 opt、l、r、c(c>0)opt、l、r、c(c>0),以空格隔开。

    若 oopt=0opt=0,表示将位于[l,r][l,r]的之间的数字都加 cc。

    若 opt=1opt=1,表示询问[l,r][l,r]中 cc 的前驱的值(不存在则输出 −1−1)。

    Output

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

    保证所有数据在int范围内

    Sample Input

    4
    1 2 2 3
    0 1 3 1
    1 1 4 4
    0 1 2 2
    1 1 2 4
    Sample Output

    3
    -1
    Hint

    SOLUTION:

    最初一看到这个题的这种格式就准备用树状数组来做的,可是做到询问前驱的时候就发现做不好了,然后就瞄了一下了学长的题解,才发现要用分块做,分块呢我只知道它的模板,~~~(我真的好菜哎^^),然后我就套模板一写,可提交就T了,中间又错了好多次,最后又看了看学长的代码,学长是用的vector来存每一块的数,左右两端残块暴力处理,中间的整块(如果有的话)一块块的处理,这时更新的时候就没有对原数组直接+val,而是维护一个lazy数组,表示每一块要加的数是多少,比较的时候用val-lazy[i]与原数组比较,如果有比它小的最大的数则存在前缀,且为找到的原数组的那个数+lazy[i],否则就没有。。。。。。。最后明白这才叫分块嘛,中间一块一块的处理,像我最初中间还是一个一个点处理,那分块意义何在。。。。。。。

    CODE:

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<set>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    using namespace std;
    const int maxn=100001;
    int belong[maxn],l[maxn],r[maxn],b[maxn];
    int lazy[maxn];
    int block,num;
    int n;
    vector<int>s[1010];
    void build()
    {
        block=(int)sqrt(n);
        num=n/block;
        if(n%block) num++;
        for(int i=1;i<=num;i++)
            l[i]=(i-1)*block+1,r[i]=i*block;
        r[num]=n;//
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&b[i]);
            belong[i]=(i-1)/block+1;
            s[belong[i]].push_back(b[i]);
        }
        for(int i=1;i<=belong[n];i++) sort(s[i].begin(),s[i].end());
    }
    void update(int ll,int rr,int val)
    {
        int first=belong[ll];
        int last=belong[rr];
        for(int i=ll;i<=min(belong[ll]*block,rr);i++)
            b[i]+=val;
        s[belong[ll]].clear();
        for(int i=(belong[ll]-1)*block+1;i<=min(belong[ll]*block,n);i++)
            s[belong[ll]].push_back(b[i]);
        sort(s[belong[ll]].begin(),s[belong[ll]].end());//左边残块
    
        if(belong[ll]==belong[rr]) return;//如果只有一个残块修改完毕
    
        for(int i=(belong[rr]-1)*block;i<=rr;i++)
            b[i]+=val;
        s[belong[rr]].clear();
        for(int i=(belong[rr]-1)*block+1;i<=min(belong[rr]*block,n);i++)
            s[belong[rr]].push_back(b[i]);
        sort(s[belong[rr]].begin(),s[belong[rr]].end());//右边残块
    
        for(int i=first+1;i<=last-1;i++)
            lazy[i]+=val;//中间的整块用lazy维护而不对原数组直接+
    }
    int query(int ll,int rr,int val)
    {
        int first=belong[ll];
        int last=belong[rr];
        int _max=-1;
        for(int i=ll;i<=min(r[belong[ll]],rr);i++)
            if(b[i]+lazy[belong[i]]<val)
                _max=max(_max,b[i]+lazy[belong[i]]);
        if(first!=last)
        {
            for(int i=l[belong[rr]];i<=rr;i++)
                if(b[i]+lazy[belong[i]]<val)
                  _max=max(b[i]+lazy[belong[i]],_max);
        }
        for(int i=first+1;i<=last-1;i++)
        {
            auto it=lower_bound(s[i].begin(),s[i].end(),val-lazy[i]);
            if(it==s[i].begin()) continue;
            --it;
            _max=max(_max,*it+lazy[i]);
        }
        return _max;
    }
    int main()
    {
        //ios::sync_with_stdio(0),cin.tie(0);
        while(~scanf("%d",&n)){
        build();
        int m=n;
        int op,x,y,k;
        while(m--)
        {
            scanf("%d%d%d%d",&op,&x,&y,&k);
            if(op==0)
            {
                update(x,y,k);
            }
            else if(op==1)
            {
                printf("%d
    ",query(x,y,k));
            }
        }
        }
        return 0;
    }
    

      

  • 相关阅读:
    开博了,将会定期更新博客
    C++实验二
    C++实验三
    c++第八章课后题
    c++第八章复数运算
    我的第一篇Window Live Writer日志
    Item 3: Prefer the is or as Operators to Casts(选择is或者as操作符而不是做强制类型转换)
    C# 中list的排序
    CodeSmith的应用
    using 关键字
  • 原文地址:https://www.cnblogs.com/zhangbuang/p/11178740.html
Copyright © 2020-2023  润新知