分块&&莫队学习笔记

坑太大了,一时甚至填不起来(现在只填到分块)


分块和莫队都是非常好的暴力算法,

能够让人在面对一些要求维护特殊序列的题时,可以拿到暴力高分甚至AC

甚至还多次出现在各地省选中,学了不亏的好东西

(但是还是要追求巧妙正解啊歪)


分块

分块,顾名思义,将数列切块,批量每个块上的答案信息,

询问时如果询问区间覆盖了这个块,直接统计答案信息就行了,如果没有,就把这个块暴力拆开,查询里面的具体数据。

修改时如果修改区间覆盖了这个块,给这个块打个标记,查询的时候再把标记处理给块内具体数据,如果不覆盖,就暴力拆开维护具体数据。

具体来看题。


Luogu P3870 [TJOI2009]开关

  • 有一个01序列,要求支持区间异或,区间查询的操作,强制在线。

  • 操作次数 = 序列长度 = 1e5

线段树,分块,朴素暴力都可以A

首先求出块长,然后求出块数,接着处理每个块的基本信息,然后小心维护就行了

分块细节很多,我调了一下午,这里说几个蒟蒻错了的地方

  • 打tag的时候已经处理了答案,消tag时又处理了一遍答案......

  • 没有处理区间在一个块内的情况

  • 循环的时候i就是块的下标,结果循环里面对下标又求了一次下标......

#include<bits/stdc++.h>

using namespace std ;

const int MAXN = 100010;
int n,m;
struct Block{
	int l,r;
	int ans,tag;
}blo[1000];
int fab[MAXN],a[MAXN],sq,bctr;

void set_up(){
	sq = sqrt(n);
	bctr = n/sq;
	if(n%sq) ++bctr;
//		cout<<sq<<"<-sq bctr->"<<bctr<<endl;
	for(int i=1;i<=n;i++){
		fab[i] = (i-1)/sq+1;
//			cout<<fab[i]<<" ";
	}
//		cout<<endl;
	for(int i=1;i<bctr;i++){
		blo[i].l = (i-1) * sq + 1;
		blo[i].r = i * sq; 
	}
	blo[bctr].l = (bctr-1) * sq + 1;
	blo[bctr].r = n;
//	for(int i=1;i<=bctr;i++){
//		cout<<"i = "<<i;
//		cout<<" ans = "<<blo[i].ans;
//		cout<<" tag = "<<blo[i].tag;
//		cout<<" l = "<<blo[i].l<<" r = "<<blo[i].r<<endl;
//	}
}

void pushdown(int x){//无脑push 
//	if(blo[x].tag == 0) cout<<"???"<<endl;
	for(int i=blo[x].l;i<=blo[x].r;i++)
		a[i] ^= 1;
	blo[x].tag = 0;
}

void change (int x,int y){
	if(fab[x] == fab[y]){
		if(blo[fab[x]].tag) pushdown(fab[x]);
		for(int i=x;i<=y;i++){
			a[i] ^= 1;
			blo[fab[i]].ans += ((a[i] == 1) ? 1 : -1); 
		}
		return;
	}
/*-----------same block--------*/ 
	if(blo[fab[x]].l != x){
		if(blo[fab[x]].tag) pushdown(fab[x]);
		for(int i=x;i<=blo[fab[x]].r;i++){
			a[i] ^= 1;
			blo[fab[i]].ans += (a[i] == 1) ? 1 : -1;
		}
	}
	else {
		blo[fab[x]].tag ^= 1;
		blo[fab[x]].ans = (blo[fab[x]].r - blo[fab[x]].l + 1) - blo[fab[x]].ans;
	}
/*-----------left part---------*/
	if(blo[fab[y]].r != y){
		if(blo[fab[y]].tag) pushdown(fab[y]);
		for(int i=blo[fab[y]].l;i<=y;i++){
			a[i] ^= 1;
			blo[fab[y]].ans += (a[i] == 1) ? 1 : -1;
		}
	}
	else{
		blo[fab[y]].tag ^= 1;
		blo[fab[y]].ans = (blo[fab[y]].r - blo[fab[y]].l + 1) - blo[fab[y]].ans;
	}
/*-----------right part---------*/
	for(int i=fab[x]+1;i<=fab[y]-1;i++){
		blo[i].tag ^= 1;
		blo[i].ans = (blo[i].r - blo[i].l + 1) - blo[i].ans;
	}
//	for(int i=1;i<=bctr;i++){
//		cout<<blo[i].ans<<" ";
//	}
	
	return;
/*-----------middle part--------*/
}

int ask(int x,int y){
	int ret = 0;
	
//		for(int i=1;i<=bctr;i++){
//			cout<<blo[i].ans<<" ";
//		}
	
	if(fab[x] == fab[y]){
		if(blo[fab[x]].tag) pushdown(fab[x]);
		for(int i=x;i<=y;i++){
			ret += a[i];
		}
		return ret;
	}
	
	if(x != blo[fab[x]].l) {
		if(blo[fab[x]].tag) pushdown(fab[x]);
		for(int i = x; i <= blo[fab[x]].r; i++) {
			ret += a[i];
		}
	}
	else ret += blo[fab[x]].ans;
	
//		for(int i=1;i<=bctr;i++){
//			cout<<blo[i].ans<<" ";
//		}
	
	if(y != blo[fab[y]].r) {
		if(blo[fab[y]].tag) pushdown(fab[y]);
		for(int i = blo[fab[y]].l; i <= y; i++) {
			ret += a[i];
		}
	}
	else ret += blo[fab[y]].ans;
	
//		for(int i=1;i<=bctr;i++){
//			cout<<blo[i].ans<<" ";
//		}
	
	for(int i=fab[x]+1;i<=fab[y]-1;i++){
		ret += blo[i].ans;
//		cout<<"ret = "<<ret<<endl;
//		cout<<"i = "<<i<<endl;
//		cout<<"blo[i].ans = "<<blo[i].ans<<endl;
	}
	return ret;
}

int main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	set_up();
	for(int i=1;i<=m;i++){
		int t,x,y;
		cin>>t>>x>>y;
		if(t == 0){
			change(x,y);
		}
		if(t == 1){
			cout<<ask(x,y)<<endl;
		}
//		cout<<"for this round,the ans1 and ans2 and ans3 is "<<blo[1].ans<<" "<<blo[2].ans<<" "<<blo[3].ans<<endl;
	}
	return 0;
}

百万丢人调试信息


Luogu P4879 ycz的妹子

这个题题面有坑,而且样例还查不出来,就很xie。

大概概括一下,给你一个序列,要求支持单点修改,查询前缀和为x的位置,序列总和查询

但是实际上题意要稍微复杂一点,建议去看原题

分块搞,前缀和也是整块处理,散块暴力,弱菜的我这次又调了一个下午

#include<bits/stdc++.h>

using namespace std ;

const int MAXN = 500010;
struct Block{
	int l,r,ctr;
	long long ans;
}blo[1010];
int blen,bctr,fab[MAXN],n,m;
bool book[MAXN];
long long a[MAXN];

struct Ord{
	char typ;
	int x;
	long long y;
}ord[MAXN];

void set_up(){
	blen = sqrt(n);
	bctr = n/blen;
	if(n%blen) bctr++;
	for(int i=1;i<=n;i++){
		fab[i] = (i - 1) / blen + 1;
	}
	for(int i=1;i<=n;i++){
		blo[fab[i]].ans += a[i];
	}
	for(int i=1;i<bctr;i++){
		blo[i].l = (i - 1) * blen + 1;
		blo[i].r = i * blen;
	}
	blo[bctr].l = (bctr - 1) * blen + 1;
	blo[bctr].r = n;
	for(int i=1;i<=n;i++){
		if(book[i] == true) blo[fab[i]].ctr++;
		else break;
	}
	return;
}

void add(int x,long long y){
	a[x] += y;
	blo[fab[x]].ans += y;
	return;
}

int get_pos(int x){
	int ctr=0,ret = 0;
	for(int i=1;i<=bctr;i++){
		ctr += blo[i].ctr;
		if(ctr>=x){
			ctr -= blo[i].ctr;
			ret = blo[i].l;
			break;
		}
	}
	while(1){
		ctr += book[ret];
		if(ctr == x) break;
		ret++;
	}
	return ret;
}

void del(int x){
	int tp = get_pos(x);
	blo[fab[tp]].ans -= a[tp];
	a[tp] = 0;
	if(book[tp] == true) --blo[fab[tp]].ctr;
	book[tp] = false;
	return;
}

void change(int x,int y){
	if(book[x] == false){
		book[x] = true;
		blo[fab[x]].ctr++;
	}
	blo[fab[x]].ans -= a[x];
	a[x] = y;
	blo[fab[x]].ans += a[x];
	return;
}

long long ask(){
	long long ret = 0;
	for(int i=1;i<=bctr;i++){
		ret += blo[i].ans;
	}
	return ret;
}

int main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		book[i] = true;
	}
	for(int i=1;i<=m;++i){
		cin>>ord[i].typ;
		if(ord[i].typ == 'C'){
			cin>>ord[i].x>>ord[i].y;
		}
		if(ord[i].typ == 'D'){
			cin>>ord[i].x;
		}
		if(ord[i].typ == 'I'){
			cin>>ord[i].x>>ord[i].y;
			n=max(n,ord[i].x);
		}
	}
	set_up();
	for(int i=1;i<=m;i++){
		if(ord[i].typ == 'Q'){
			cout<<ask()<<endl;
		}
		if(ord[i].typ == 'C'){
			add(ord[i].x,-ord[i].y);
		}
		if(ord[i].typ == 'D'){
			del(ord[i].x);
		}
		if(ord[i].typ == 'I'){
			change(ord[i].x,ord[i].y);
		}
	}
	return 0;
}

我好菜啊,不过这个打的挺好看的

posted @ 2019-07-24 19:31  SIN_XIII  阅读(192)  评论(0编辑  收藏  举报