分块小记

优雅的暴力

前言

如果对于一个序列,如果要求和。一个个地处理需要 O(n) 但是假如我们将序列预处理好,再算就是 O(1) 的。但要是还要修改呢?

分块就是一个折中的办法.可以做一些线段树没法做的事情.

区间加区间和

下面讲解区间加区间和的思路.

假设块长度为 len,数量为 num=n/len。那么我们可以记录数组 tagk。表示第 k 个块的标记,还有 sk 表示第 k 个块的和。

如果现在要给 [l,r] 都加上 p.

如果区间覆盖了整个块,直接tagxtagx+p,sxtagx×len (这个 len 可能受到边界的影响) 就可以了。
如果不是,直接在原数列上进行修改,即暴力。

查询的时候也是一样的.
如果区间覆盖了整个块,直接 ansans+sx.
如果不是,直接暴力求和.


代码(luogu P3372)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+10;
int a[N],id[N];
int len;
int s[N], tag[N];
#define l(i) ((i)*(len-1)+1)
#define r(i) (min((i)*len,n))
void bupd(int l,int r,int c) {
	for(int i=l;i<=r;i++) {
		a[i]+=c,s[id[i]]+=c;
	}
}
void upd(int l,int r,int c) {
    if(id[l] == id[r]) {
        bupd(l,r,c);
        return ;
    }
    bupd(l,r(id[l])),bupd(l(id[r]),r);
    for(int i=id[l]+1;i<id[r];i++) {
        tag[i] += c, s[i] += len*c;
    }
}
void bqry(int l,int r,int mod) {
	int p = 0;
	for(int i=l;i<=r;i++) {
		(p += a[i] + tag[id[i]]) %= mod;
	}
	return p;
}
int query(int l,int r,int mod) {
    int ans = 0;
    if(id[l] == id[r]) {
        return bqry(l,r,mod);
    }
    (ans += bqry(l,r[id[l]],mod) + bqry(l(id[r]),r,mod))%=mod;
    for(int i=id[l]+1;i<id[r];i++) {
        (ans+=s[i])%=mod;
    }
    return ans%mod;
}
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    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];
    }
    while(n--) {
        int o,l,r,k;
        cin>>o>>l>>r>>k;
        if(o==0) upd(l,r,k);
        else cout<<query(l,r,k+1)<<"\n";
    } 
    return 0;
}

本文作者:cjrqwq

本文链接:https://www.cnblogs.com/yfzqwq/p/18080228

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   cjrqwq  阅读(6)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
展开
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.