题目链接:https://loj.ac/problem/6278
解题思路:
分块维护区间递增序列。
对于修改,边界暴力后修改两个边界的递增序列使其正确,完整块打标记
对于询问,边界暴力+块内二分
修改的复杂度 O(sqrt(N) + 2*sqrt(N)*log(sqrtN))
询问复杂度 O (sqrt(N) + 2*sqrt(N))
所以整体大概是 O (q*sqrt(N)*log(sqrtN)) //玄学计算复杂度,不太准
所以分块大概是用来处理几万的数据量的。(5e4的数据什么的)
话不多说,直接看板子就得啦。(要交C++17才能过,不过代码本身应该是没问题的,自己测试过上面的数据了)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define pb push_back
#define ll long long
using namespace std;
const int N = 5e4+5;
const int M = 500;
int blo,n;
int bl[N];
ll a[N];
ll atag[M];
vector<ll>ve[M];
void reset(int x)
{
ve[x].clear();
for (int i=blo*(x-1)+1;i<=min(blo*x,n);i++){
ve[x].pb(a[i]);
}
sort(ve[x].begin(),ve[x].end());
}
void modify(int l,int r,ll c)
{
for (int i=l;i<=min(bl[l]*blo,r);i++)
a[i] += c;
reset(bl[l]);
if (bl[l]!=bl[r]){
for (int i=(bl[r]-1)*blo+1;i<=r;i++)
a[i] += c;
reset(bl[r]);
}
for (int i=bl[l]+1;i<=bl[r]-1;i++)
atag[i] += c;
}
int query(int l,int r,ll c)
{
int ans = 0;
for (int i=l;i<=min(bl[l]*blo,r);i++)
if (a[i]+atag[bl[l]]<c) ans++;
//printf("ans=%d\n",ans);
if (bl[l]!=bl[r]){
for (int i=(bl[r]-1)*blo+1;i<=r;i++)
if (a[i]+atag[bl[r]]<c) ans++;
}//printf("ans=%d\n",ans);
for (int i=bl[l]+1;i<=bl[r]-1;i++){
ans += lower_bound(ve[i].begin(),ve[i].end(),c-atag[i]) - ve[i].begin();
}//printf("ans=%d\n",ans);
return ans;
}
int main()
{
/*
freopen("C:/Users/DELL/Desktop/a1.in", "r", stdin);
freopen("C:/Users/DELL/Desktop/my.out", "w", stdout);
*/
scanf("%d",&n);
blo = sqrt(n);
///init
//for (int i=1;i<=(n-1)/blo+1;i++) ve[i].clear(),atag[i]=0;
for (int i=1;i<M;i++){
ve[i].clear();
atag[i] = 0;
}
for (int i=1;i<=n;i++){
scanf("%I64d",a+i);
bl[i] = (i-1)/blo + 1;
ve[bl[i]].pb(a[i]);
}
for (int i=1;i<=bl[n];i++) sort(ve[i].begin(),ve[i].end());
int op,l,r;
ll c;
int q = n;
while (q--){
scanf("%d %d %d %I64d",&op,&l,&r,&c);
if (op==0){
modify(l,r,c);
/*
for (int i=1;i<=n;i++){
printf("%I64d ",a[i]+atag[bl[i]]);
if (bl[i+1]!=bl[i]) printf("] [");
}
printf("\n");
*/
}
else printf("%d\n",query(l,r,c*c));
}
return 0;
}