[ACM]数列分块1-9

14____数列分块

14.1____数列分块1(区间加法,单点查询)

image-20210803104118041

题解:

​ 这个题就是数列分块的板子题,对区间做区间修改单点查询,区间修改为区间加法,线段树同样可以做,但是代码就很复杂了,我们用 tag 来维护分块区间的相同和

​ 在update的时候,如果在同一区间内,那么我们直接 \(O(n)\)​​​解决 ,如果不在的话,那么位于左端和右端的区间,我们都直接 \(O(n)\)​​​ 处理, 在中间的区间,我们直接同一加到 tag 上,这 tag 数组有点像 线段树的 lazy标记,但是不会 push_down,查询的时候,我们直接叠加上去就可以了。

#include <bits/stdc++.h>
using namespace std;

const int N = 5e4 + 10;

int a[N],s[N],tag[N],len,id[N];

void add(int l,int r,int c)
{
    int sid = id[l] , eid = id[r];
    if( sid == eid ){
        for(int i = l ;i <= r; i++){
            a[i] += c, s[sid] += c;
        }
        return ;
    }

    for(int i = l ; id[i] == sid ; i++) a[i] += c,s[ sid ] += c;
    for(int i = sid + 1 ; i < eid ; i++) tag[i] += c,s[ i ] += c * len;
    for(int i = r ; id[i] == eid ; i--) a[i] += c,s[eid] += c;
}

int query(int x)
{
    return a[x] + tag[ id[x] ];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int n;
    cin >> n;
    len = sqrt(n);

    for(int i = 1; i <= n ; i++){
        cin >> a[i];
        id[i] = (i - 1) / len + 1;
        s[ id[i] ] += a[i];
    }

    for(int i = 0 ; i < n ; i++){
        int op,x,y,z;
        cin >> op >> x >> y >> z;
        if( op == 0 ){
            add(x,y,z);
        }else{
            cout << query(y) << endl;
        }
    }
    return 0;
}

14.2____数列分块2

image-20210803104611858

#include <bits/stdc++.h>
using namespace std;

const int N = 5e5 + 10;
int a[N],len,id[N],tag[N],vec[N];

/// id * len 当前块最后一个元素
/// (id - 1) * len + 1 当前块的第一个元素
/// 注意lower_bound 是左闭右开 (取不到第二个值)
/// 开区间使用符号小括号()表示,闭区间使用符号中括号[]表示
void reset(int tes,int tee)   /// x为所在分块的编号
{
    for(int i = tes ; i <= tee ; i++)   vec[i] = a[i];
    sort(vec + tes,vec + tee + 1);
}

void update(int l,int r,int c)
{
    int sid = id[l],eid =  id[r];

    if(sid == eid){
        for(int i = l ; i <= r; i++)    a[i] += c;
        reset( (sid - 1)*len +1 ,sid *len );
        return ;
    }
    ///
    for(int i = l; id[i] == sid ; i++)    a[i] += c;
    reset( (sid - 1)*len +1 ,sid *len );

    for(int i = sid + 1 ; i < eid ; i++)    tag[i] += c;

    for(int i = r ; id[i] == eid ; i--)  a[i] += c;
    reset( (eid - 1)*len +1 ,eid *len );

}

int query(int l,int r,int x)
{
    int sid = id[l], eid = id[r] , cnt = 0;

    if( sid == eid){
        for(int i = l ; i <= r; i++)
            if( a[i] + tag[ id[i] ] < x ) cnt++;
        return cnt;
    }
    ///
    int te = sid * len ;
    for(int i = l ; i <= te ; i++)
        if( a[i] + tag[ sid ] < x ) cnt ++;

    for(int i = sid + 1; i < eid ; i++){
        int tes = (i - 1)*len + 1, tee = i *len;
        cnt += int( lower_bound( vec + tes , vec + tee + 1, x - tag[i]) - vec - tes );
    }

    te = (eid - 1)  * len + 1;
    for(int i = te ; i <= r; i++)
        if( a[i] + tag[ eid ] < x ) cnt++;

    return cnt;

}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int n;
    cin >> n;
    len = sqrt(n);

    for(int i = 1; i <= n ; i++){
        cin >> a[i];
        id[i] = (i - 1) /len + 1;
    }

    for(int i = 1 ; i <= id[n] ; i++ ){
        reset((i - 1)*len + 1, i * len);
        /// 注意如果是最后一个分块,要单独弄
        if (i == id[n]) {
            reset((i - 1)*len + 1, n);
        }
    }

    for(int i = 0; i < n ; i++){
        int op,x,y,z;
        cin >> op >> x >> y >> z;
        if( op == 0 ){
            update(x,y,z);
        }else{
            cout << query(x,y,z*z) <<endl;
        }
    }

    return 0;
}

线段树

#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
#define Mid ((T[rt].l + T[rt].r)>>1)
#define L ( T[rt].l)
#define R (T[rt].r)
using namespace std;

const int N = 5e4 + 10;
void file() {
#ifdef ONLINE_JUDGE
#else
    freopen("d:/workProgram/test.in", "r", stdin);
#endif
}

struct Node {
    int l, r;
    int ma, mi;
    int lazy ;
} T[N << 2];

int a[N];

void up(int rt) {
    T[rt].ma = max(T[ls].ma, T[rs].ma);
    T[rt].mi = min(T[ls].mi, T[rs].mi);
}

void down(int rt) {
    if (T[rt].lazy != 0) {
        T[ls].lazy += T[rt].lazy;
        T[rs].lazy += T[rt].lazy;
        T[ls].ma += T[rt].lazy;
        T[rs].ma += T[rt].lazy;
        T[ls].mi += T[rt].lazy;
        T[rs].mi += T[rt].lazy;
        T[rt].lazy = 0;
    }
}

void build(int rt, int l, int r) {
    T[rt] = {l, r, 0, 0, 0};

    if (l == r) {
        T[rt].ma = T[rt].mi = a[l];
        return ;
    }

    int mid = (l + r) >> 1;
    build(ls, l, mid), build(rs, mid + 1, r);
    up(rt);
}

void rangeUpdate(int rt, int l, int r, int val) {
    if (l <= L && r >= R) {
        T[rt].ma += val;
        T[rt].mi += val;
        T[rt].lazy += val;
        return ;
    }

    down(rt);

    if (l <= Mid)
        rangeUpdate(ls, l, r, val);

    if (r > Mid)
        rangeUpdate(rs, l, r, val);

    up(rt);
}

int rangeQuery(int rt, int l, int r, int x) {
    if (T[rt].mi >= x)
        return 0;

    if (l <= L  && r >= R && T[rt].ma < x) {
        return T[rt].r - T[rt].l + 1;
    }

    down(rt);
    int cnt = 0;

    if (l <= Mid)
        cnt += rangeQuery(ls, l, r, x);

    if (r > Mid)
        cnt += rangeQuery(rs, l, r, x);

    return cnt;
}

int main() {

    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    //file();
    int n;
    cin >> n;

    for (int i = 1 ; i <= n ; i++) {
        cin >> a[i];
    }

    build(1, 1, n);

    for (int i = 0; i < n ; i++) {
        int op, l, r, x;
        cin >> op >> l >> r >> x;

        //cout << "--"<< i <<endl;
        if (op == 0) {
            rangeUpdate(1, l, r, x);
        } else {
            cout << rangeQuery(1, l, r, x * x) << endl;
        }
    }

    //system("pause");

    return 0;
}

14.3____数列分块3(求区间中比数x,小的第一个数)

线段树 TLE了...可能是维护最大值和最小值,必须要递归到最深才可以更新答案,他数据全是查询,就把线段树卡死了...

还是贴一下代码,我看了统计,前面的没有一个是线段树

#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
#define Mid ( (T[rt].l + T[rt].r) >> 1)
#define L (T[rt].l)
#define R (T[rt].r)
#define INF  (-1)*0x3f3f3f3f3f
using namespace std;

void file()
{
    #ifdef ONLINE_JUDGE
    #else
        freopen( "d:/workProgram/test.in","r",stdin );
    #endif
}


typedef long long ll;
const ll N = 1e5 + 10;

struct Node
{
    ll l,r;
    ll ma,mi,lazy;
}T[N << 2];

ll a[N],ans;   /// ans = -1*(0x3f3f3f3f);

void push_up(ll rt)
{
    T[rt].ma = max( T[ls].ma , T[rs].ma );
    T[rt].mi = min( T[ls].mi , T[rs].mi );
}

void push_down(ll rt)
{
    if( T[rt].lazy != 0 ){
        T[ls].ma += T[rt].lazy;
        T[rs].ma += T[rt].lazy;
        T[ls].mi += T[rt].lazy;
        T[rs].mi += T[rt].lazy;
        T[ls].lazy += T[rt].lazy;
        T[rs].lazy += T[rt].lazy;
        T[rt].lazy = 0;
    }
}

void build(ll rt,ll l,ll r)
{
    T[rt] = {l,r,0,0,0};
    if(l == r){
        T[rt].ma = T[rt].mi = a[l];
        //cout << "LL:" << l << "a:" << a[l] <<endl;
        return;
    }

    build(ls,l,Mid),build(rs,Mid+1,r);
    push_up(rt);
    //cout<< "l:" << L << "R:" << R << "ma:" << T[rt].ma <<endl;
}

void update(ll rt,ll l,ll r,ll val)
{
    if( l <= L && r >= R ){
        T[rt].ma += val;
        T[rt].mi += val;
        T[rt].lazy += val;
        return ;
    }

    push_down(rt);
    if( l <= Mid )update(ls,l,r,val);
    if( r > Mid) update(rs,l,r,val);
    push_up(rt);
}

void query(ll rt,ll l,ll r,ll val)
{
    if( T[rt].mi > val || T[rt].ma < ans) return ;
    if( l <= L && r >= R && T[rt].ma < val ){
        ans = max( ans , T[rt].ma);
        return ;
    }
    if( l <= L && r >= R && L == R && T[rt].ma >= val )   return ;
    if( R < l || L > r || L == R) return ;
    //cout<< "l:" << L << "R:" << R << "ma:" << T[rt].ma <<endl;

    push_down(rt);
    if( l <= Mid ) query(ls,l,r,val);
    if( r >= Mid ) query(rs,l,r,val);
    push_up(rt);

}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    //file();
    ll n;
    cin >> n;

    for(int i = 1; i <= n ;i++){
        cin >> a[i];
    }

    build(1,1,n);

    for(int i = 0 ; i < n ;i++){
        int op,x,y,z;
        cin >> op >> x >> y >> z;
        if( op == 0 ){
            update(1,x,y,z);
        }else{
            ans = INF;
            query(1,x,y,z);
            //cout << "ans:" << ans << endl;
            if( ans == INF ){
                cout << -1 <<endl;
            }else{
                cout << ans <<endl;
            }
        }
    }

    return 0;
}

14.5____数列分块5(区间开方,区间求和)

我的自己的做法是,开两个数组来维护,sum[]维护区间和,num[]来维护区间开了几次方

num[]的由来是因为 $$2^{32}$$ 大小的数,最多开7次就到1了,之后就没必要开了,所以在add中,对于一个整块如果 num[i] > 7 || sum[i] <= len 那么我们对这个块就不需要进行操作了

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;

int a[N], sum[N], num[N], id[N], len;

void down(int l, int r) {
    for (int i = l; i <= r;  i++)
        a[i] = sqrt(a[i]);
}

int get(int l, int r) {
    int res = 0 ;

    for (int i = l ; i <= r; i++)
        res += a[i];

    return res;
}

void update(int l, int r) {
    int L = id[l], R = id[r];

    if (L == R) {
        for (int i = l ; i <= r; i++)
            a[i] = sqrt(a[i]);

        return ;
    }

    for (int i = l ; id[i] == L ; i++)
        a[i] = sqrt(a[i]);

    for (int i = r ; id[i] == R ; i--)
        a[i] = sqrt(a[i]);

    sum[L] = get((L - 1) * len + 1, L * len);
    sum[R] = get((R - 1) * len + 1, R * len);

    for (int i = L + 1; i < R; i ++) {
        if (num[i] > 7 || sum[i] <= len)
            continue;
        else {
            down((i - 1)*len + 1, i * len);
            num[i]++;
            sum[i] = get((i - 1) * len + 1, i * len);
        }
    }
}

int query(int l, int r) {
    int L = id[l], R = id[r], res = 0;

    if (L == R) {
        for (int i = l ; i <= r ; i++)
            res += a[i];

        return res;
    }

    for (int i = l ; id[i] == L ; i++)
        res += a[i];

    for (int i = r ; id[i] == R ; i--)
        res += a[i];

    for (int i = L + 1; i < R; i ++)
        res += sum[i];

    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int n;
    cin >> n;
    len = sqrt(n);

    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        id[i] = (i - 1) / len + 1;
        sum[ id[i] ] += a[i];
    }

    for (int i = 0 ; i < n ; i++) {
        int op, x, y, z;
        cin >> op >> x >> y >> z;

        if (op == 0) {
            update(x, y);
        } else {
            cout << query(x, y) << "\n";
        }
    }

    return 0;
}

另一个dalao的做法提交记录 #601465 - LibreOJ (loj.ac)

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+10, M = 500;
int a[N],ma[M],L[M],R[M],id[N],len,sum[M];

inline void update_sum(int l,int r, int k)
{
    for(int i = l ; i <= r; i ++)   sum[k] -= a[i], a[i] = sqrt(a[i]),sum[k] += a[i];
}

inline void update_max(int k)
{
    ma[k] = sqrt( ma[k] );
    for(int i = L[k] ; i <= R[k]; i++)    ma[k] = ma[k] > a[i]? ma[k]:a[i];
}

inline void update(int l,int r)
{
    if( id[ l ] == id[ r ] ){
        if( ma[ id[l] ] <= 1 )  return ;

        update_sum(l,r,id[l]);
        update_max(id[l]);

        return ;
    }

    if( ma[ id[l] ] > 1 )
        update_sum( l,R[id[l]],id[l] ),update_max( id[l] );

    if( ma[ id[r] ] > 1 )
        update_sum( L[ id[r] ] , r, id[r] ), update_max( id[r] );

    for(int i = id[l] + 1 ; i < id[r] ; i++ ){
        if( ma[ i ] > 1 ) update_sum( L[i],R[i],i ),ma[i] = sqrt(ma[i]);
    }

}

int query(int l,int r)
{
    int res = 0;
    if( id[l] == id[r] ){
        for(int i = l ; i <= r; i ++)   res += a[i];
        return res;
    }

    for(int i = l ; i <= R[ id[l] ] ; i ++) res += a[i];
    for(int i = L[ id[r] ] ; i <= r;  i ++) res += a[i];

    for(int i = id[ l ] + 1 ; i < id[r] ; i++)  res += sum[i];

    return res;
}


int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int n;
    cin >> n ;
    len = sqrt(n);
    for(int i = 1; i <= n ; i++){
        cin >> a[i];
        id[i] = ( i - 1 ) / len + 1;
        sum[ id[i] ] += a[i];
        ma[ id[i] ] = ma[ id[i] ] > a[i] ? ma[ id[i] ] : a[i];
    }

    for(int i = 1 ; i <= id[n] ; i++){
        L[i] = (i-1)*len+1 , R[i] = min( i*len , n );
    }

    for(int i = 0; i < n ; i++){
        int op,x,y,z;
        cin >> op >> x >> y >>z;
        if( op == 0 ){
            update( x,y );
        } else {
            cout << query( x,y ) <<endl;
        }
    }



    return 0;
}

posted @ 2021-10-13 09:20  Hoppz  阅读(60)  评论(0编辑  收藏  举报