【滚动训练】 LOJ 6278. 数列分块入门 2 (分块)

题意

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。

题解

分块由于每块内有 n 个元素,在暴力的程度上保证了复杂度。于是我们主要考虑的是如何维护块内信息。
根据分块入门 1的经验,需要一个tag来标记对整个块进行的操作和为多少。由于这道题要进行计数,于是问题就转换成如何快速统计区间的数目问题。

方案一

维护一个块内最小和块内最大,如果块内最小元素都大于等于 x,那么整个块对答案的贡献为0;反之,如果块内的最大元素都小于 x,那么整个块对答案的贡献是块内元素个数。这样对于某些块,可以做到 O ( 1 ) 的查询。
对于查询时最左侧和最右侧的块,暴力修改即可,对于中间的块,根据最大最小值进行查询和更新。
这个trick是对于此题数据跑的最快的。

方案二

我们对每个块内元素进行排序,这样查询的时候就可以二分找到一个位置。通过坐标的关系来查询求解每个块对于答案的贡献。
需要注意的是,排序是对原数组的副本进行排序,副本块内有序的性质方便进行查询,更新并不是很方便。因为首先需要对原数组进行更新(暴力更新和打tag),暴力更新的部分还需要将这一部分拷贝到副本,再排序。所以相对上一种方案,排序复杂度较高。

总结

对于较强的数据,方案一并不是一个很好的选择,出题人可以故意查询值置于块内最大值和最小值之间,使得每一块都暴力查询,这样显然复杂度退化为 O ( n ) 。方案二的复杂度相对稳定一些。
对于本题数据来说:
方案一 : Accepted 100 591 ms 792 KiB
方案二 : Accepted 100 2277 ms 1176 KiB

代码

方案一

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int nmax = 5e4 + 10 ;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mm = sqrt(5e4 + 100) + 10;
int n,m,num,block;
ll data[nmax],tag[mm],mmax[mm],mmin[mm];
int L[mm],R[mm],belong[nmax];
void init(){
    block = 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,
        mmax[i] = -INF,
        mmin[i] = INF;
}
int main() {
    scanf("%d",&n);
    init();
    for(int i = 1;i<=n;++i){
        scanf("%lld",&data[i]);
        belong[i] = (i-1) / block + 1;
        mmax[belong[i]] = max(mmax[belong[i]],data[i]);
        mmin[belong[i]] = min(mmin[belong[i]],data[i]);
    }
    int op,l,r; ll w;
    for(int i = 1;i<=n;++i){
        scanf("%d %d %d %lld",&op,&l,&r,&w);
        if(op == 0){
            if(belong[r] - belong[l] <= 1){
                for(int i = l;i<=r;++i) {
                    data[i] += w;
                    mmax[belong[i]] = max(mmax[belong[i]],data[i] + tag[belong[i]]);
                    mmin[belong[i]] = min(mmin[belong[i]],data[i] + tag[belong[i]]);
                }
            }else{
                for(int i = l;i<=R[belong[l]];++i) {
                    data[i] += w;
                    mmax[belong[i]] = max(mmax[belong[i]],data[i] + tag[belong[i]]);
                    mmin[belong[i]] = min(mmin[belong[i]],data[i] + tag[belong[i]]);
                }
                for(int i = L[belong[r]];i<=r;++i) {
                    data[i] += w;
                    mmax[belong[i]] = max(mmax[belong[i]],data[i] + tag[belong[i]]);
                    mmin[belong[i]] = min(mmin[belong[i]],data[i] + tag[belong[i]]);
                }
                for(int i = belong[l] + 1; i<= belong[r] -1 ;++i){
                    tag[i] += w;
                    mmax[i] += w;
                    mmin[i] += w;
                }
            }
        }else{
            int ans = 0;ll c = w * w;
            if(belong[r] - belong[l] <= 1){
                for(int i = l;i<=r;++i)
                    if(data[i] + tag[belong[i]] < c) ans++;
            }else{
                for(int i = l;i<=R[belong[l]];++i){
                    if(data[i] + tag[belong[i]] < c) ans ++;
                }
                for(int i = L[belong[r]];i<=r;++i){
                    if(data[i] + tag[belong[i]] < c) ans ++;
                }
                for(int i = belong[l] + 1;i<= belong[r]-1;++i){
                    if(mmax[i]<c) ans += block;
                    else if(mmin[i] >= c) continue;
                    else{
                        for(int j = L[i];j<=R[i];++j){
                            if(data[j] + tag[i] < c) ans++;
                        }
                    }
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

方案二



#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int nmax = 5e4 + 100;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mm = sqrt(5e4 + 100) + 10;
int n;
ll data[nmax],tag[mm],temp[nmax];
int belong[nmax],L[mm],R[mm],block,num;
void init(){
    block = 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;
}
int main() {
    scanf("%d",&n);
    init();
    for(int i = 1;i<=n;++i){
        scanf("%lld",&data[i]);
        temp[i] = data[i];
        belong[i] = (i-1) / block + 1;
    }
    for(int i = 1;i<=num;++i) sort(temp + L[i],temp + R[i] + 1);
    int op,l,r; ll w;
    for(int j = 1;j<=n;++j){
        scanf("%d %d %d %lld",&op,&l,&r,&w);
        if(op == 0){
            if(belong[r] - belong[l] <= 1){
                for(int j = l;j<=r;++j) data[j] += w;
                for(int j = L[belong[l]]; j<=R[belong[r]];++j) temp[j] = data[j];
                sort(temp + L[belong[l]], temp + R[belong[l]] + 1);
                if(belong[r] != belong[l]) sort(temp + L[belong[r]], temp + R[belong[r]] + 1);
            }else{
                for(int j = l;j<=R[belong[l]];++j) data[j] += w;
                for(int j = L[belong[r]];j<=r;++j) data[j] += w;
                for(int j = belong[l] + 1 ;j<=belong[r]-1;++j) tag[j] += w;
                for(int j = L[belong[l]];j<=R[belong[l]];++j) temp[j] = data[j];
                for(int j = L[belong[r]];j<=R[belong[r]];++j) temp[j] = data[j];
                sort(temp + L[belong[l]], temp + R[belong[l]] + 1);
                sort(temp + L[belong[r]], temp + R[belong[r]] + 1);
            }
        }else{
            int ans = 0; ll c = w * w;
            if(belong[r] - belong[l] <= 1){
                for(int j = l;j<=r;++j)
                    if(data[j] + tag[belong[j]] < c) ans ++;
            }else{
                for(int j = l;j<=R[belong[l]];++j)
                    if(data[j] + tag[belong[j]] < c) ans ++;
                for(int j = L[belong[r]];j<=r;++j)
                    if(data[j] + tag[belong[j]] < c) ans ++;
                for(int j = belong[l]+1;j<=belong[r]-1;++j){;
                    int pos = lower_bound(temp + L[j],temp + R[j] + 1, c - tag[j]) - (temp + L[j]);
                    ans += pos;
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/pengwill97/article/details/80898250