一言(ヒトコト)

这是我马上要写的分块博客,但是目前已经只有一点点

从来没有学过
一无所知

感觉超级像线段树和树状数组

上代码

首先init

void init(){
	int len=sqrt(n);//这个就是块的大小
	while(1){
		cnt++;
		l[cnt]=(cnt-1)*len+1;
		r[cnt]=cnt*len;
		if(r[cnt]>n){//超过n了就不再分块了
			r[cnt]=n;
			break;
		}
	}
	for(int i=1;i<=cnt;++i){
		for(int j=l[i];j<=r[i];++j){
			pos[j]=i;//这个给每个区间都打上相同的标记
		}
	}
	Elysia;
}

区间add

void add(int l1,int r1,LL x){
	if(pos[l1]==pos[r1]){//如果在一个块中,那没辙,只能一点点加
		for(int i=l1;i<=r1;++i){
			a[i]+=x;
		} 
		Elysia;
	}
	for(int i=l1;i<=r[pos[l1]];++i){//加上左边的散块
		a[i]+=x;
	} 
	for(int i=l[pos[r1]];i<=r1;i++){//and右边的散块
		a[i]+=x;
	}
	for(int i=pos[l1]+1;i<pos[r1];i++){//加上区间的整块
		tag[i]+=x;
	}
}

这个令人着迷的题目,我交了4遍才过
先把代码贴上
解释代码就好了

区间加区间求和

#include<bits/stdc++.h>
//这个分块咋麽纳麽像线段树和树状数组啊
//放弃了绝望了没觉得对不起谁最对不起的还是自己吧
#define Elysia return
#define Elysia return
using namespace std;
typedef long long LL;
const int N=3e5+10;
int n;
LL a[N];
int l[N],r[N],cnt;
LL tag[N];
int pos[N];
LL s[N];
void init(){
	int len=sqrt(n);
	while(1){
		cnt++;
		l[cnt]=(cnt-1)*len+1;
		r[cnt]=cnt*len;
		if(r[cnt]>n){
			r[cnt]=n;
			break;
		}
	}
	for(int i=1;i<=cnt;++i){
		for(int j=l[i];j<=r[i];++j){
			pos[j]=i;
			s[i]+=a[j];//这个和上面的不一样,这个数组表示每一个块的和,先在这里初始化
		}
	}
	Elysia;
}

void add(int l1,int r1,LL x){
	if(pos[l1]==pos[r1]){
		for(int i=l1;i<=r1;++i){
			a[i]+=x;
		} 
		s[pos[l1]]+=(r1-l1+1)*x;//我们要实时更新这个数组,每一次值的改变我们都要更新
		//r1-l1+1是这个区间的长度 x是这个区间加的值  长度*值就是我们这个区间总体增加的量
		Elysia;
	}
	for(int i=l1;i<=r[pos[l1]];++i){
		a[i]+=x;
	} 
	s[pos[l1]]+=(r[pos[l1]]-l1+1)*x;//这个也要变,注意这个区间是l1所在的区间 所以是s[pos[l1]]
	for(int i=l[pos[r1]];i<=r1;i++){
		a[i]+=x;
	}
	s[pos[r1]]+=(r1-l[pos[r1]]+1)*x;//这个同样要变,但是这个区间是r1所在的区间
	for(int i=pos[l1]+1;i<pos[r1];i++){
		tag[i]+=x;
//		s[i]+=(r[i]-l[i]+1)*x;
	}
}

LL query(int l1,int r1){
	LL sum=0;
	if(pos[l1]==pos[r1]){
		for(int i=l1;i<=r1;++i){
			sum+=a[i];
		} 
		sum+=(r1-l1+1)*tag[pos[l1]];//记得加上tag,后面同理
		Elysia sum;
	}
	for(int i=l1;i<=r[pos[l1]];++i){
		sum+=a[i];
	} 
	sum+=(r[pos[l1]]-l1+1)*tag[pos[l1]];//tag
	for(int i=l[pos[r1]];i<=r1;i++){
		sum+=a[i];
	}
	sum+=(r1-l[pos[r1]]+1)*tag[pos[r1]];//tag
	for(int i=pos[l1]+1;i<pos[r1];i++){
		sum+=(s[i]+tag[i]*(r[i]-l[i]+1));
	}	
	Elysia sum;
}


int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	init();
	for(int i=1;i<=n;i++){
		int op,l1,r1;
		LL c;
		cin>>op>>l1>>r1>>c;
		if(op==0){
			add(l1,r1,c);
		}
		else{
			LL ans=(query(l1,r1));
			while(ans<0)ans+=c+1;//我愚蠢但一定不会出错的取模
			cout<<ans%(c+1)<<"\n";
//			cout<<ans<<"\n";
		}
	}
	Elysia 0;
} 
/*
4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4
*/
/*
1
4
*/

P13982 数列分块入门 7
这题要求区间加 区间乘 单点查询

void pushdown(int x){
    for(int i=(x-1)*len+1;i<=min(x*len,n);i++){
        tag[i]=(mul[x]*tag[i]%10007+add[x])%10007;//先处理乘,再做加法;
		//有点像线段树的时候,标记下传
    }
    add[x]=0;
    mul[x]=1;
}

void addfun(int a,int b,ll c){//加法要保持自己之前已经没有乘法了,所以要先pushdown
    pushdown(bl[a]);
    for(int i=a;i<=min(bl[a]*len,b);i++){
        tag[i]=(tag[i]+c)%10007;
    }
    if(bl[a]!=bl[b]){//不是一个块中
        pushdown(bl[b]);
        for(int i=(bl[b]-1)*len+1;i<=b;i++){
            tag[i]=(tag[i]+c)%10007;//正常的+
        }
    }
    for(int i=bl[a]+1;i<=bl[b]-1;i++){
        add[i]=(add[i]+c)%10007;
    }
}

void mulfun(int a,int b,ll c){
    pushdown(bl[a]);//也要保证自己前面没有+了
    for(int i=a;i<=min(bl[a]*len,b);i++){
        tag[i]=(tag[i]*c)%10007;
    }
    if(bl[a]!=bl[b]){
        pushdown(bl[b]);
        for(int i=(bl[b]-1)*len+1;i<=b;i++){
            tag[i]=(tag[i]*c)%10007;
        }
    }
    for(int i=bl[a]+1;i<=bl[b]-1;i++){
        add[i]=(add[i]*c)%10007;
        mul[i]=(mul[i]*c)%10007;
    }
}

啊啊啊尖叫这题终于AC了
这个题细节好多
每一个块都排序,块内的值如果有变动就要重新排序
那找< \(c^2\)的就可以二分做
其实就这么多,但是请看代码实现细节,包括顺序和什么时候该sort

#include<bits/stdc++.h>
#define Elysia return
#define Elysia return
using namespace std;
typedef long long LL;
const int N=3e5+10;
int n;
LL a[N];
int l[N],r[N],cnt;
LL tag[N];
int pos[N];
vector<LL> t[N];
int len;
void init(){//普通的建块(应该叫这个,懂我意思吧?
	len=sqrt(n);
	while(1){
		cnt++;
		l[cnt]=(cnt-1)*len+1;
		r[cnt]=cnt*len;
		if(r[cnt]>n){
			r[cnt]=n;
			break;
		}
	}
	for(int i=1;i<=cnt;i++){
		for(int j=l[i];j<=r[i];j++){
			pos[j]=i;
			t[i].push_back(a[j]);
		}
	}
	for(int i=1;i<=cnt;i++){
		sort(t[i].begin(),t[i].end());
	}
	Elysia;
}
void solve(int x){
	t[x].clear();
	for(int i=l[x];i<=r[x];i++){
		t[x].push_back(a[i]);//要放到排好序的数组里,因为我们要保留原来的数据,所以要新建一个数组
	}
	sort(t[x].begin(),t[x].end());//排序
}
void add(int l1,int r1,LL x){
	if(pos[l1]==pos[r1]){//在同一个块里
		for(int i=l1;i<=r1;++i){
			a[i]+=x;
		} 
		solve(pos[l1]);//改变了哪个块,哪个块就要重新排序
		Elysia;
	}
	for(int i=l1;i<=r[pos[l1]];++i){
		a[i]+=x;
	} 
	solve(pos[l1]);//改变了哪个块,哪个块就要重新排序
	for(int i=l[pos[r1]];i<=r1;i++){
		a[i]+=x;
	}
	solve(pos[r1]);///改变了哪个块,哪个块就要重新排序
	for(int i=pos[l1]+1;i<pos[r1];i++){
		tag[i]+=x;//注意这里的块其实并没有改变(a[i]并没有改变
		//我们用tag存下每一个大块整体改变的值就好了
	}
}

LL query(int l1,int r1,LL c){
	LL ans=0;
	if(pos[l1]==pos[r1]){
		for(int i=l1;i<=r1;++i){
			if(a[i]+tag[pos[l1]]<c*c){//注意要加tag,tag是大块加的值,每一个点都要加(只不过一起保存在tag里面了
			//注意我们要用块的下标去找tag ,不要和**的我一样写成tag[i]
				ans++;
			}
		} 
		Elysia ans;
	}
	for(int i=l1;i<=r[pos[l1]];++i){
		if(a[i]+tag[pos[l1]]<c*c){
		//注意我们要用块的下标去找tag ,不要和**的我一样写成tag[i]
			ans++;
		}
	} 
	for(int i=l[pos[r1]];i<=r1;i++){
		if(a[i]+tag[pos[r1]]<c*c){
		//注意我们要用块的下标去找tag ,不要和**的我一样写成tag[i]
			ans++;
		}
	}
	for(int i=pos[l1]+1;i<pos[r1];i++){
		int p=lower_bound(t[i].begin(),t[i].end(),c*c-tag[i])-t[i].begin();
		//这里二分去找就可以了,注意这里的tag就是tag[i]了,因为i表示的就是块的下标
		ans+=p;
	}
	Elysia ans;
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	init();
	for(int i=1;i<=n;i++){
		int op,l,r;
		LL c;
		cin>>op>>l>>r>>c;
		if(op==0){
			add(l,r,c);
		}
		else{
			cout<<query(l,r,c)<<"\n";
		}
	}
	Elysia 0;
} 
/*
4
1 2 2 3
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
*/

持续更新中……
os这玩意儿真的不会被卡吗

posted @ 2026-07-27 19:21  冬冬猫  阅读(4)  评论(0)    收藏  举报