题解 P3372 【【模板】线段树 1】

发一篇不需要O2就能过的分块。

基本思路:

分块的思路,大段维护,小段朴素。

维护几个数组:

  • 区块\(block[maxn]\)
  • 懒标记\(tag[maxn]\)
  • 真实数据\(data[maxn]\)

更新时

假设我们涉及到的区块的编号区间是\([lb,rb]\)真实数据范围为\([l,r]\)

那么,我们在区块\([lb+1,rb-1]\)区间,给\(block[i]\)加上\(\Delta k\) 并且打上懒标记 \(=\Delta k\)。在真实数据的\([l,lb\times N]\)\([rb\times N-N+1,r]\)区间的每个\(data[i]\)加上\(\Delta k\)

查询时

同样的,对于区块区间\([lb+1,rb-1]\)直接累加就好,对于\(l b\)区间和\(rb\)区间单独处理即可。

上丑陋无比的代码。注意特判\(l\)\(r\)在一个区块区间的情况。

#include<bits/stdc++.h>
using namespace std;
template< class ccf >inline ccf qr(ccf a) {
    char c=getchar();
    ccf x=0;
    int q=1;
    while(c<48||c>57)
    q=c==45?-1:q,c=getchar();
    while(c>=48&&c<=57)
    x=x*10+c-48,c=getchar();
    return q==-1?-x:x;
}
const int maxn=100055;
typedef long long ll;
ll data[maxn];
ll tag[maxn];
ll blk[maxn];
int n;
int m;
int N;
int adj;
#define RP(t,a,b) for(register int t=(a),edd=(b);t<=edd;t++)
int t1,t2;
ll t3;
int temp;

void add(int l,int r,ll k) {
    int lb=(l+N-1)/N;
    int rb=(r+N-1)/N;
    if(lb!=rb) {

    RP(t,l,N*lb)
        data[t]+=k,blk[lb]+=k;
    RP(t,N*rb-N+1,r)
        data[t]+=k,blk[rb]+=k;
    RP(t,lb+1,rb-1)
        tag[t]+=k,blk[t]+=N*k;
    } else
    RP(t,l,r)
        data[t]+=k,blk[t]+=k;
}

ll sum(int l,int r) {
    ll ret=0;
    int lb=(l+N-1)/N;
    int rb=(r+N-1)/N;
    RP(t,lb+1,rb-1)
    ret+=blk[t];
    if(tag[lb])
    RP(t,N*lb-N+1,N*lb)
        data[t]+=tag[lb];
    tag[lb]=0;
    if(tag[rb])
    RP(t,N*rb-N+1,min(N*rb,n))
        data[t]+=tag[rb];
    tag[rb]=0;
    if(lb!=rb) {
    RP(t,l,N*lb)
        ret+=data[t];
    RP(t,N*rb-N+1,r)
        ret+=data[t];
    } else
    RP(t,l,r)
        ret+=data[t];
    return ret;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    n=qr(1);
    m=qr(1);
    RP(t,1,n)
    data[t]=qr(1ll);
    N=sqrt(n);
    RP(t,1,n/N)
    RP(i,1,N)
    blk[t]+=data[(t-1)*N+i];
    RP(t,1,n%N)
    blk[n/N+1]+=data[(n/N)*N+t];
    RP(t,1,m) {
    temp=qr(1);
    if(temp==1) {
        t1=qr(1);
        t2=qr(1);
        t3=qr(1ll);
        add(t1,t2,t3);
    } else {
        t1=qr(1);
        t2=qr(1);
        printf("%ld\n",sum(t1,t2));
    }
    }
    return 0;
}

转载于:https://www.cnblogs.com/winlere/p/10308076.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值