分块

分块复杂度略微证明:

\[n:序列的长度;\\ S:将这个序列分成S块;\\ C:分成的每个块内有C个元素;\\ x:查询或修改时,需要操作的整块数;\\ y:查询或修改时,需要操作的零散区间中元素个数;\\ L:查询或修改时,操作序列的长度。 \\ S*C=n我们肯定要让x+y最小\\ 最坏情况操作区间为[2,n-1],此时需要查询S−2个块和2C−2个元素\\,即x=S−2,y=2C−2。 x+y=S−2+2C−2=S+2C−4。\\忽略常数,得x+y=S+C 因SC=n,则C=nS。\\ 代入,得x+y=S+nS≥2\sqrt n\\ 当且仅当S=nS,即S=\sqrt n时,等号成立。 所以在最坏时,时间复杂度为O(\sqrt n)。常数不超过3\\ 在一般情况下时,可以得出L=xC+y。 移项,得x=L−yC。\\ 因0<L\le n,0<y<2C,则0<L−y<n−2C。\\ 因C>0,则\frac{L-y}{C}<\frac{n-2C}{C}=\frac{SC-2C}{C}=S-2<S \\ 故x+y<S+2C,忽略常数,x+y<S+C。\\ 因SC=n,S+C\ge 2\sqrt{SC}。 当且仅当S=C,即S=C=\sqrt n时,等号成立。\\ 此时x+y<\sqrt n。 综上,分块的时间复杂度为O(\sqrt n),常数不超过3。证毕 \]

转载该文:https://blog.csdn.net/HeRaNO/article/details/55219560?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase

小花的题解qwq
分块相对其他数据结构优点:通用,码量较小,直观,不易出错,兼容性强

分块实现的基本框架:
划分块,预处理,操作或查询。
操作或查询通常为4步:
1.判断要操作或是查询的区间是否在一个块内
2.若在一个块内,暴力操作或查询
3.若不在一个块内,将除了最左边和最右边这两个块外其余的块进行整体的操作,即直接对块打上修改标记之类的
4.单独暴力处理最左边的块和最右边的块

同时分块的块内还可以使用别的数据结构或是操作以实现要求或进一步优化复杂度

把数列分成长度若干长度不超过\(\lfloor\sqrt n\rfloor\)的段,其中第\(i\)段的左端点为\((i-1)\lfloor\sqrt n\rfloor+1\),右端点为\(min(i\lfloor\sqrt n\rfloor,n)\)

预处理数组\(sum\),其中\(sum[i]\)表示\(i\)段区间和,设\(add[i]\)表示第\(i\)段的增量标记,起初\(add[i]=0\)

对于指令区间加操作

1.\(l\)\(r\)同时位于第\(i\)段内,把\(A[l]...A[r]\)全部\(+d\),令\(sum[i]+=d*(r-l+1)\)

2.\(l\)处于第\(p\)\(r\)处于第\(q\)段.

(1).对于\(i\in[p+1,q-1],令add[i]+=d\) (2).对于开头,结尾不足一整段的两部分,按照与第一种情况相同的方法朴素的更新

对于区间和的操作

1.\(l与r\)同时位于\(i\)段内,则\((A[l]+A[l+1]+...+A[r])+(r-l+1)*add[i]\)就是答案

2.\(l\)处于第\(p\)段,\(r\)处于第\(q\)段, \(ans+=sum[i]+add[i]*len[i],len[i]为第i段长度\)

(2)对于开头结尾不足一整段,暴力维护

\(复杂度O((N+Q)*\sqrt N)\),核心思想:大段维护,小段暴力

libre oj分块入门1

#include<cstdio>
#include<cmath>
#include<iostream>
#define maxn 50005
using namespace std;
int a[maxn],add[250],pos[maxn],block;
//a原数组,pos每个位置属于哪段 

void adde(int x,int y,int z){
	 for (int i = x; i <= min(pos[x] * block, y); i++)//处理左端点所在的不完整的块
        a[i] += z;
    if (pos[x] != pos[y])//如果左右端点不在一个块里处理右端点所在的不完整的块
        for (int i = (pos[y] - 1) * block + 1; i <= y; i++)
            a[i] += z;
    for (int i = pos[x] + 1; i <= pos[y] - 1; i++)//处理中间完整的区间
        add[i] += z;
}
int main(){
	int n,opt,c,l,r;
	scanf("%d",&n);
	block = sqrt(n);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	pos[i] = (i - 1) / block + 1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d",&opt,&l,&r,&c);
		if(!opt) adde(l,r,c);
		else printf("%d\n",a[r]+add[pos[r]]);
	}
}

分块入门2

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n, block;
const int MAXN = 5e4 + 10;
const int block_ = 250;
int w[MAXN], b[MAXN], lazy[block_];
vector<int> v[block_];
void spread(int x)  //将不完整的块压入vector重新排序
{
    v[x].clear();
    int l = (x - 1) * block + 1, r = min(x * block, n);
    for (int i = l; i <= r; i++) v[x].push_back(w[i]);
    sort(v[x].begin(), v[x].end());
}
void add(int x, int y, int z) {
    for (int i = x; i <= min(b[x] * block, y); i++) w[i] += z;
    if (b[x] != b[y]) {
        for (int i = (b[y] - 1) * block + 1; i <= y; i++) w[i] += z;
        spread(b[y]);
    }
    for (int i = b[x] + 1; i <= b[y] - 1; i++) lazy[i] += z;
    spread(b[x]);
}
ll ans(int x, int y, int z) {
    int sum = 0;
    int big = z * z;
    for (int i = x; i <= min(b[x] * block, y); i++)
        if (w[i] + lazy[b[i]] < big)
            sum++;
    if (b[x] != b[y])
        for (int i = (b[y] - 1) * block + 1; i <= y; i++)
            if (w[i] + lazy[b[i]] < big)
                sum++;
    for (int i = b[x] + 1; i <= b[y] - 1; i++)
        sum += lower_bound(v[i].begin(), v[i].end(), big - lazy[i]) -
               v[i].begin();  // STL大法好,自带二分查找,查找第一个大于等于查找元素的下标(vector从0开始)
    return sum;
}
int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    block = sqrt(n);
    for (int i = 1; i <= n; i++) {
        cin >> w[i];
        b[i] = (i - 1) / block + 1;
        v[b[i]].push_back(w[i]);
    }
    for (int i = 1; i <= b[n]; i++) sort(v[i].begin(), v[i].end());
    for (int i = 1; i <= n; i++) {
        int opt, x, y, z;
        cin >> opt >> x >> y >> z;
        if (!opt)
            add(x, y, z);
        else
            cout << ans(x, y, z) << endl;
    }
    return 0;
}

分块入门4/线段树板子

ll n,Q,len,a[maxn],num,pos[maxn];
#define h 320
ll tag[h],sum[h],ri[h],le[h];
void build(){
	num = n /len; if(n % len) ++num;//共num块 
	for(int i = 1;i <= n;++i) a[i] = read(),pos[i] = (i-1)/len + 1;//注意减1
	for(int i = 1;i <= num;++i)
		le[i] = (i-1)*len + 1,ri[i] = i*len;
	ri[num] = n;
	for(int i = 1;i <= num;++i){
		for(int j = le[i];j <= ri[i];++j)
			sum[i] += a[j];
	}
}
inline void adde(int l,int r,int c){
	if(pos[l] == pos[r]){
		for(int i = l;i <= r;++i) a[i] += c;
		sum[pos[l]] += (r - l + 1) * c; return;
	}
	for(int i = l;i <= ri[pos[l]];++i)
		a[i] += c;
	sum[pos[l]] += (ri[pos[l]] - l + 1) * c;
	for(int i = pos[l]+1;i < pos[r];++i) tag[i] += c;
	for(int i = le[pos[r]];i <= r;++i)
		a[i] += c;
	sum[pos[r]] += (r - le[pos[r]] + 1) * c;
}
ll query(int l,int r){
	ll ans = 0;
	if(pos[l] == pos[r]){ 
		for(int i = l;i <= r;++i) ans += a[i] + tag[pos[i]];
		return ans;
	} 
	for(int i = l;i <= ri[pos[l]];++i)
		ans += a[i] + tag[pos[l]];
	for(int i = pos[l]+1;i < pos[r];++i)
		ans += tag[i] * len + sum[i];
	for(int i = le[pos[r]];i <= r;++i)
		ans += a[i] + tag[pos[r]];
	return ans;
}
int main(){
	cin >> n >> Q; len = 3*sqrt(n)/2; build();
	ll op,l,r,c;
	while(Q--){
		op = read(); l = read(); r = read(); 
		if(op & 1) c = read(),adde(l,r,c);
		else printf("%lld\n",query(l,r));
	}
}

P2574xor艺术

int n,m,a[maxn],len,pos[maxn]; char s[maxn];
int sum[450],tag[450],num,l[450],r[450];
void build(){
	num = n / len; if(n % len) ++num;
	for(int i = 0;i < n;++i)
		a[i+1] = (s[i] == '1'),pos[i+1] = i/len + 1;
	for(int i = 1;i <= num;++i)
		l[i] = (i-1) * len + 1,r[i] = i*len;
	r[num] = n;
	for(int i = 1;i <= num;++i)
		for(int j = l[i];j <= r[i];++j)
			if(a[j]) ++sum[i];
}
void change(int le,int ri){
	if(pos[le] == pos[ri]){//暴力维护小区间 
		for(int i = le;i <= ri;++i){ 
			sum[pos[i]] += (a[i] == 1) ? -1 : 1;
			a[i] ^= 1;
		}  return;
	}
	for(int i = le;i <= r[pos[le]];++i){//左碎块暴力维护 
		sum[pos[i]] += (a[i] == 1) ? -1 : 1;
		a[i] ^= 1;
	}
	for(int i = pos[le]+1;i < pos[ri];++i)//大段打tag 
		tag[i] ^= 1; 
	for(int i = l[pos[ri]];i <= ri;++i){//右碎块暴力维护 
		sum[pos[i]] += (a[i] == 1) ? -1 : 1;
		a[i] ^= 1;
	}
}
int query(int le,int ri){
	int ans = 0;
	if(pos[le] == pos[ri]){//暴力统计小区间 
		for(int i = le;i <= ri;++i)
			if(a[i] ^ tag[pos[i]]) ++ans;//a[i]=1,tag=0 || a[i]=0,tag=1有贡献 
		return ans;
	}
	for(int i = le;i <= r[pos[le]];++i)//左碎块暴力统计 
		if(a[i] ^ tag[pos[i]]) ++ans;
	for(int i = pos[le]+1;i < pos[ri];++i)//大段直接用sum 
		ans += (tag[i]) ? (len - sum[i]) : sum[i];
	for(int i = l[pos[ri]];i <= ri;++i)//右碎块暴力统计 
		if(a[i] ^ tag[pos[i]]) ++ans;
	return ans;
}
int main(){
	cin >> n >> m; cin >> s; len = sqrt(n); int op,ri,le;
	build();
	while(m--){
		op = read(); le = read(); ri = read();
		if(op) printf("%d\n",query(le,ri));
		else change(le,ri);
	}
}
posted @ 2020-07-18 19:47  INFP  阅读(198)  评论(2编辑  收藏  举报