LOJ #6279. 数列分块入门 3-分块(区间加法、查询区间内小于某个值x的前驱(比其小的最大元素))

#6279. 数列分块入门 3

内存限制:256 MiB时间限制:1500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 3  

题目描述

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

输入格式

第一行输入一个数字 nn。

第二行输入 nn 个数字,第 ii 个数字为 a_iai​,以空格隔开。

接下来输入 nn 行询问,每行输入四个数字 \mathrm{opt}opt、ll、rr、cc,以空格隔开。

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

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

输出格式

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

样例

样例输入

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

样例输出

3
-1

数据范围与提示

对于 100\%100% 的数据,1 \leq n \leq 100000, -2^{31} \leq \mathrm{others}1≤n≤100000,−231≤others、\mathrm{ans} \leq 2^{31}-1ans≤231−1。

 

代码:

  1 //#6279. 数列分块入门 3-区间加法,查询区间内小于某个值x的前驱(比其小的最大元素)
  2 #include<bits/stdc++.h>
  3 using namespace std;
  4 typedef long long ll;
  5 const int maxn=1e5+10;
  6 
  7 int n,m;
  8 ll a[maxn],b[maxn],pos[maxn],tag[maxn];
  9 
 10 void rechange(int x)
 11 {
 12     for(int i=(x-1)*m+1;i<=min(x*m,n);i++){
 13             b[i]=a[i];
 14     }
 15     sort(b+(x-1)*m+1,b+min(x*m,n)+1);
 16 }
 17 
 18 void update(int l,int r,ll c)
 19 {
 20     if(pos[l]==pos[r]){
 21         for(int i=l;i<=r;i++)
 22             a[i]+=c;
 23         rechange(pos[l]);
 24     }
 25     else{
 26         for(int i=l;i<=pos[l]*m;i++)
 27             a[i]+=c;
 28         rechange(pos[l]);
 29         for(int i=pos[l]+1;i<=pos[r]-1;i++)
 30             tag[i]+=c;
 31         for(int i=(pos[r]-1)*m+1;i<=r;i++)
 32             a[i]+=c;
 33         rechange(pos[r]);
 34     }
 35 }
 36 
 37 ll query(int l,int r,ll c)
 38 {
 39     ll ans=-1;
 40     if(pos[l]==pos[r]){
 41         for(int i=l;i<=r;i++){
 42             if(a[i]+tag[pos[l]]<c){
 43                 ans=max(ans,a[i]+tag[pos[l]]);
 44             }
 45         }
 46     }
 47     else{
 48         for(int i=l;i<=pos[l]*m;i++){
 49             if(a[i]+tag[pos[l]]<c){
 50                 ans=max(ans,a[i]+tag[pos[l]]);
 51             }
 52         }
 53         for(int i=pos[l]+1;i<=pos[r]-1;i++){
 54             int cnt=c-tag[i];
 55             int ret=lower_bound(b+(i-1)*m+1,b+i*m+1,cnt)-b-1;
 56             //cout<<ret<<" "<<(i-1)*m<<endl;
 57             if(ret!=(i-1)*m)
 58                 ans=max(ans,b[ret]+tag[i]);
 59         }
 60         for(int i=(pos[r]-1)*m+1;i<=r;i++){
 61             if(a[i]+tag[pos[r]]<c){
 62                 ans=max(ans,a[i]+tag[pos[r]]);
 63             }
 64         }
 65     }
 66     return ans;
 67 }
 68 
 69 int main()
 70 {
 71     scanf("%d",&n);
 72     m=sqrt(n);
 73     for(int i=1;i<=n;i++){
 74         scanf("%d",&a[i]);
 75         b[i]=a[i];
 76         pos[i]=(i-1)/m+1;
 77     }
 78     for(int i=1;i<=m+1;i++)
 79         sort(b+(i-1)*m+1,b+min(i*m,n)+1);
 80     for(int i=1;i<=n;i++){
 81         int op,l,r;
 82         ll c;
 83         scanf("%d%d%d%lld",&op,&l,&r,&c);
 84         if(op==0){
 85             update(l,r,c);
 86         }
 87         else{
 88             printf("%lld\n",query(l,r,c));
 89         }
 90     }
 91 }
 92 
 93 
 94 /*
 95 10
 96 1 3 4 2 5 7 11 3 5 1
 97 0 1 5 1
 98 1 1 7 2
 99 0 3 9 1
100 1 4 8 7
101 1 1 10 6
102 1 3 5 3
103 1 5 10 7
104 1 6 10 6
105 1 2 7 4
106 1 2 7 5
107 
108 -1
109 4
110 4
111 -1
112 6
113 4
114 -1
115 4
116 */

 

 

上一篇:中国剩余定理


下一篇:LOJ #6278. 数列分块入门 2-分块(区间加法、查询区间内小于某个值x的元素个数)