LibreOJ 6278 数列分块入门 2(分块区间加法,二分)
解题思路
询问区间小于某个数个个数显然可以用二分来做,但是如果配合上区间加法就有些复杂了。即使对每个区间排序,用标记来代替修改,但是对于边缘的数据来说,需要暴力修改,而暴力修改后打破区间的有序性。那就暴力修改之后再重新排序(没错,就是这么狠(笑
代码
const int maxn = 1e5+10;
struct INFO {
int num, i;
INFO(int a=0, int b=0): num(a), i(b) {}
bool operator < (const INFO &a) const {
return num < a.num;
}
} arr[maxn];
int n,sz,num,l[maxn],r[maxn],bl[maxn],lazy[maxn];
void build() {
num = (n+sz-1)/sz;
for (int i = 1; i<=num; ++i) {
l[i] = (i-1)*sz+1, r[i] = i*sz;
}
r[num] = n;
for (int i = 1; i<=n; ++i) bl[i] = (i-1)/sz+1;
for (int i = 1; i<=num; ++i) sort(arr+l[i],arr+r[i]+1);
}
void update(int a, int b, int c) {
if (bl[a]==bl[b]) {
for (int i = l[bl[a]]; i<=r[bl[a]]; ++i)
if (arr[i].i>=a && arr[i].i<=b) arr[i].num += c;
sort(arr+l[bl[a]], arr+r[bl[a]]+1);
return;
}
for (int i = bl[a]+1; i<bl[b]; ++i) lazy[i] += c;
for (int i = l[bl[a]]; i<=r[bl[a]]; ++i)
if (arr[i].i>=a) arr[i].num += c;
for (int i = l[bl[b]]; i<=r[bl[b]]; ++i)
if (arr[i].i<=b) arr[i].num += c;
sort(arr+l[bl[a]], arr+r[bl[a]]+1);
sort(arr+l[bl[b]], arr+r[bl[b]]+1);
}
int find(int a, int b, int c) {
int ans = 0;
if (bl[a]==bl[b]) {
for (int i = l[bl[a]]; i<=r[bl[a]]; ++i)
if (arr[i].i>=a && arr[i].i<=b && arr[i].num+lazy[bl[i]]<c) ++ans;
return ans;
}
for (int i = bl[a]+1; i<bl[b]; ++i)
ans += lower_bound(arr+l[i],arr+r[i]+1,INFO(c-lazy[i],0))-arr-l[i];
for (int i = l[bl[a]]; i<=r[bl[a]]; ++i)
if (arr[i].i>=a && arr[i].num+lazy[bl[i]]<c) ++ans;
for (int i = l[bl[b]]; i<=r[bl[b]]; ++i)
if (arr[i].i<=b && arr[i].num+lazy[bl[i]]<c) ++ans;
return ans;
}
int main() {
scanf("%d",&n);
for (int i = 1; i<=n; ++i) {
scanf("%d",&arr[i].num);
arr[i].i = i;
}
sz = sqrt(n); build();
for (int i = 1,op,l,r,c; i<=n; ++i) {
scanf("%d%d%d%d",&op,&l,&r,&c);
if (!op) update(l,r,c);
else printf("%d\n",find(l,r,c*c));
}
return 0;
}