题解:P13978 数列分块入门 3
前言
和数列分块入门
声明:此处没有想宣传本人题解,看别人更好懂的也可以。
思路
首先,区间查询和区间修改思路基本一样,这个应该可以不说吧。
然后,发现题目要我们求一个数
那么我们也可以用一样的思路(这里再叙述一遍):
很容易想到二分,也就是说,假定每个块内有序,如果当前是一整个块,那么可以直接二分解决前驱;反之,则一个一个枚举判断,取最大值。
那么,现在的问题就是:如何让每个块内有序。
容易发现我们不可以直接排序,因为直接排序会打乱每个数的顺序,修改的时候就会出错。所以,我们可以再开一个数组,记录的就是每个块内排好序的序列。
那么,初始化很简单了,就是每个块内的数直接排序。那么修改操作呢?
首先,如果修改了这一整个块,那么由于是整体全都加上了一个数,所以排序结果不变,只是数值整体变化了,我们还是按照数列分块入门
然后,问题就在如果修改的不是一个整块上了。那么很容易发现,每次修改最多只有两个这样的小部分,因此直接修改以后再次排序即可。
时间复杂度:
Code
代码细节比较多,最好自己写,可以借鉴一下,但是请不要抄袭。
如果没做过数列分块入门 甚至代码我都是直接拷过来改一下的。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[200005],b[200005],num[455],len,n;
void update(int now){//对每个块排序
for(int i = (now-1)*len+1;i<=min(now*len,n)/*一定要注意角块!*/;i++){
b[i] = a[i];
}
sort(b+(now-1)*len+1,b+min(now*len,n)+1);
}
signed main() {
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
len = sqrt(n);
for(int i = 1; i<=n; i++) {
cin>>a[i];
}
for(int i = 1; i<=len+(len*len!=n)/*后面的条件语句是判断有没有角块的,如果有,块的数量要增加一个*/; i++) {
update(i);//初始化每个块
}
for(int i = 1; i<=n; i++) {
int op,l,r,c;
cin>>op>>l>>r>>c;
int nl,nr;
nl = (l-1)/len+1;
nr = (r-1)/len+1;
if(!op) {//修改操作
if(nl == nr) {
for(int j = l; j<=r; j++) {
a[j] = a[j]+c;
}
update(nl);//注意非完整块修改完以后要排序
} else {
for(int j = l; j<=nl*len; j++) {
a[j] = a[j]+c;
}
update(nl);
for(int j = nl+1; j<nr; j++) {
num[j] = num[j]+c;
}
for(int j = (nr-1)*len+1; j<=r; j++) {
a[j] = a[j]+c;
}
update(nr);
}
} else {
int ans;
ans = -1e18;//千万,千万不要设置成-1!因为a[i]可能为负!
if(nl == nr){
for(int j = l;j<=r;j++){
if(a[j]+num[nl]<c){
ans = max(ans,a[j]+num[nl]);//不要忘记加上这个块的权值
}
}
}else{
for(int j = l;j<=nl*len;j++){
if(a[j]+num[nl]<c){
ans = max(ans,a[j]+num[nl]);
}
}
for(int j = nl+1;j<nr;j++){
int w;
w = lower_bound(b+(j-1)*len+1,b+j*len+1,c-num[j])-b-1;
if(w>(j-1)*len){//注意:如果这个块内没有比x(即代码中的c)更小的,一定要特殊判断。否则你取的最大值就是上一个块的最后一个数。
ans = max(ans,b[w]+num[j]);
}
}
for(int j = (nr-1)*len+1;j<=r;j++){
if(a[j]+num[nr]<c){
ans = max(ans,a[j]+num[nr]);
}
}
}
if(ans == -1e18){
ans = -1;
}
cout<<ans<<"\n";
}
}
return 0;
}