数列分块思想

分块思想

  1. 我们一般把m个元素分为一块,所以总共的块数就是n/m块, 一般情况下,我们取m=sqrt(n)

  2. 对于区间加操作,可以先对两边进行暴力处理,然后对中 间的整块的部分进行加减,我们累加在块的标记上,然后我们每次查询的时候只要每个元素的值加上这个块的标记值,就可以得到我们的答案了

  3. 简单来说区间加
    暴力的方法处理不完整的块
    完整块标记法

A . 数列分块入门1

在这里插入图片描述

思路

原链接

首先对一个序列进行分块,假设序列为:1,2,3,4,5,6,7,8,9。那么每一个块的大小为3,所以,123属于第一块,456为第二块,789为第三块。然后用一个数组pos[]去记录第i个元素所处于哪个块内。例如:pos[2]=1,即2这个元素位于第一块。
假设块的大小为len,那pos[i]=(i-1)/len+1

这样我们就把原序列分成n/len(+1)个块了,每次区间操作的时候,不在一整块的元素暴力循环一下,在一个整块的,就开一个数组t[]记录第i块还没有加的数是多少。

注意下l,r在同一块的处理

Code:

/*

*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
#define ll long long
using namespace std;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
const ll  INFll = 0x3f3f3f3f3f3f3f3fLL;
const int INF= 0x7ffffff;
const int mod=998244353;
const int maxn =5e5+7;
int a[maxn];
int pos[maxn];
int mmax[maxn];
int t[maxn];
int len;
void f(int l,int r,int c) {
	for(int i=l; i<=min(pos[l]*len,r); i++) {//处理左边的边角块
		a[i]+=c;
	}
	if(pos[l]!=pos[r]) {//处理右边的边角块
		for(int i=r; i>=max((pos[r]-1)*len+1,l); i--) {
			a[i]+=c;
		}
	}
	for(int i=pos[l]+1; i<=pos[r]-1; i++) { //处理中间剩余的整块
		t[i]+=c;
	}
}
int main() {
	int n;
	cin>>n;
	len=sqrt(n);//分成的每个块的大小,一般长度都为sqrt(n)
	for(int i=1; i<=n; i++) {
		cin>>a[i];
		pos[i]=(i-1)/len+1;//表示第i个数字在第几块里

	}
	for(int i=1; i<=n; i++) {
		int opt,l,r,c;
		cin>>opt>>l>>r>>c;
		if(opt==0)
			f(l,r,c);
		else
			cout<<a[r]+t[pos[r]]<<endl;
	}

	return 0;
}

B . 数列分块入门 2

在这里插入图片描述

思路

原链接
区间加法 + 区间查询
具体看代码解释

  1. 由于在暴力修改不完整的块时,破坏了块原来的单调性,所以需要另外建立一个数组,对其进行重新赋值,并进行排序

  2. 对于完整块的修改,使用二分

二分计算方法回顾

原文链接
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。

在从小到大的排序数组中,

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

在从大到小的排序数组中,重载lower_bound()和upper_bound()

lower_bound( begin,end,num,greater() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num,greater() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

Code:

/*
利用二分查找的方法在一个排好序的数组中进行查找

lower_bound( begin,end,num)
从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字
找到返回该数字的地址,不存在则返回end
通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
using namespace std;
typedef unsigned long long ull;
typedef pair<int, int>PII;
const int INF= 0x7ffffff;
const int mod=998244353;
const int maxn =1e6+7;

int n;
int l[maxn];//第i块左区间端点
int r[maxn];//第i块右区间端点
int pos[maxn];//第i个元素位于第几块
int a[maxn],b[maxn];
int opt,ll,rr,c;
int vis[maxn]; 

void f(int L,int R,int C){
	int pl=pos[L];//左端点所在的块数 
	int pr=pos[R];//右端点所在的块数
	
	if(pl==pr){
		for(int i=L;i<=R;i++){
			a[i]+=C;//可能破坏单调性 
		}
		for(int i=l[pl];i<=r[pl];i++){
			b[i]=a[i];//b数组重新赋值 
		}
		sort(b+l[pl],b+1+r[pl]);//重新排序 
	}
	
	else{
		for(int i=L;i<=r[pl];i++){//修改左侧不完整的块 
			a[i]+=C;
		}
		for(int i=l[pl];i<=r[pl];i++){//b数组进行重新赋值 
			b[i]=a[i];
		}
		sort(b+l[pl],b+1+r[pl]);//重新排序 
		
		for(int i=l[pr];i<=R;i++){//右侧 
			a[i]+=C;
		} 
		for(int i=l[pr];i<=r[pr];i++){
			b[i]=a[i];
		}
		sort(b+l[pr],b+r[pr]+1);
		
		for(int i=pos[L]+1;i<=pos[R]-1;i++){//中间完整部分 
			vis[i]+=C;
		}
	} 
}

int ans(int L,int R,int C){
	int pl=pos[L];//左端点所在的块数 
	int pr=pos[R];//右端点所在的块数
	int cnt=0;
	
	if(pl==pr){//两端点相等时的情况 
		for(int i=L;i<=R;i++){
			if(a[i]+vis[pl]<C) 
				cnt++;
		}
		return cnt;
	}
	
	for(int i=L;i<=r[pl];i++){暴力查询左侧不完整的块 
		if(a[i]+vis[pl]<C)
			cnt++;
	}
	for(int i=l[pr];i<=R;i++){暴力查询右侧不完整的块 
		if(a[i]+vis[pr]<C)
			cnt++;
	}
	//二分查找
	for(int i=pos[L]+1;i<=pos[R]-1;i++){
		cnt+=lower_bound(b+l[i],b+r[i]+1,C-vis[i])-b-l[i];
	}
	return cnt;
}
int main() {
	cin>>n;
	int len=sqrt(n);//分成的每个块的大小,一般长度都为sqrt(n)
	int num=ceil(n*1.0/len);//大概有多少块
	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<=n; i++) {
		cin>>a[i];
		b[i]=a[i];
		pos[i]=(i-1)/len+1;//表示第i个数字在第几块里
	}
	for(int i=1; i<=num; i++) {
		sort(b+l[i],b+r[i]+1);//对每个块进行排序
	}
	for(int i=1; i<=n; i++) {
		cin>>opt>>ll>>rr>>c;
		if(opt==0) {
			f(ll,rr,c);
		} else {
			cout<<ans(ll,rr,c*c)<<endl;
		}
	}
	return 0;
}

C . 数列分块入门 3

在这里插入图片描述

思路

与数列分块入门 2题目思路大致相同。

  1. 更新操作对【L,R】内元素+c,查询操作对【L,R】查找小于c的最大元素。

  2. 根据分块的思想,对于不完整的块用暴力的方法进行记录,对每一块进行排序,再用二分查找出最大的小于c的数。

注意数据类型的变化

Code:

/*

*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
#define ll long long
using namespace std;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
const ll  INFll = 0x3f3f3f3f3f3f3f3fLL;
const int INF= 0x7ffffff;
const int mod=998244353;
const int maxn =1e6+7;
ll n;
ll a[maxn];
ll L[maxn],R[maxn];
ll pos[maxn];
ll b[maxn];
ll vis[maxn];
ll len;


void f(int l,int r,int c) {
	ll pl=pos[l];
	ll pr=pos[r];
	if(pl==pr) {

		for(int i=l; i<=r; i++)
			a[i]+=c;
		for(int i=L[pl]; i<=R[pl]; i++)
			b[i]=a[i];
		sort(b+L[pl],b+1+R[pl]);

	} else {

		for(int i=l; i<=R[pl]; i++)
			a[i]+=c;
		for(int i=L[pl]; i<=R[pl]; i++)
			b[i]=a[i];
		sort(b+L[pl],b+1+R[pl]);


		for(int i=L[pr]; i<=r; i++)
			a[i]+=c;
		for(int i=L[pr]; i<=R[pr]; i++)
			b[i]=a[i];
		sort(b+L[pr],b+1+R[pr]);


		for(int i=pos[l]+1; i<=pos[r]-1; i++)
			vis[i]+=c;
	}
}

ll query(int l,int r,int c) {
	int ans=-1;
	ll pl=pos[l];
	ll pr=pos[r];
	ll t;
	if(pl==pr) {
		for(int i=l; i<=r; i++) {
			t=a[i]+vis[pl];
			if(t>ans && t<c)
				ans=t;
		}
	} else {
		for(int i=l; i<=R[pl]; i++) {
			t=a[i]+vis[pl];
			if(t>ans && t<c)
				ans=t;
		}
		for(int i=L[pr]; i<=r; i++) {
			t=a[i]+vis[pr];
			if(t>ans && t<c)
				ans=t;
		}
		for(int i=pos[l]+1; i<=pos[r]-1; i++) {
			int pos=lower_bound(b+L[i],b+R[i]+1,c-vis[i])-b-1;
			t=b[pos]+vis[i];
			if(t>ans && t<c)
				ans=t;
		}
	}
	return ans;
}

int main() {
	cin>>n;
	len=sqrt(n);
	ll num=ceil(n*1.0/len);//大概有多少块
	for(int i=1; i<=n; i++) {
		cin>>a[i];
		b[i]=a[i];
	}
	for(int i=1; i<=num; i++) {
		L[i]=(i-1)*len+1;
		R[i]=i*len;
		sort(b+L[i],b+1+R[i]);
		vis[i]=0;
		for(int j=L[i]; j<=R[i]; j++)
			pos[j]=i;
	}
	while(n--) {
		ll opt,x,y,w;
		cin>>opt>>x>>y>>w;
		if(opt==0)
			f(x,y,w);
		else
			cout<<query(x,y,w)<<endl;
	}
	return 0;
}

D . 数列分块入门 4

在这里插入图片描述

思路

求区间和,建立一个sum数组储存每一块的总和
区间加法+区间求和
具体实现见代码

注意题目的数据类型,long long

Code:

/*

*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>

using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int>PII;

const int INF= 0x7ffffff;
const int mod=998244353;
const int maxn =1e6+7;
int n;
int l[maxn];//第i块左区间端点
int r[maxn];//第i块右区间端点
int pos[maxn];//第i个元素位于第几块
ll a[maxn],sum[maxn];
int opt,lll,rr,c;
ll vis[maxn];
int len;
ll cnt;
void f(int L,int R,int C) {
	int pl=pos[L];//左端点所在的块数
	int pr=pos[R];//右端点所在的块数
	if(pl==pr) {
		for(int i=L; i<=R; i++) {
			a[i]+=C;
			sum[pl]+=C;
		}
	} else {
		for(int i=L; i<=r[pl]; i++) { //修改左侧不完整的块
			a[i]+=C;
			sum[pl]+=C;
		}

		for(int i=l[pr]; i<=R; i++) { //右侧
			a[i]+=C;
			sum[pr]+=C;
		}
		for(int i=pos[L]+1; i<=pos[R]-1; i++) { //中间完整部分
			vis[i]+=C;
		}
	}
}

ll ans(int L,int R,int C) {
	int pl=pos[L];//左端点所在的块数
	int pr=pos[R];//右端点所在的块数
	cnt=0;
	if(pl==pr) { //两端点相等时的情况
		for(int i=L; i<=R; i++) {
			cnt=(cnt+a[i]+vis[pl])%C;
		}
		return cnt%C;
	}
	for(int i=L; i<=r[pl]; i++) {//左端
		cnt=(cnt+a[i]+vis[pl])%C;
	}
	for(int i=l[pr]; i<=R; i++) {//右端
		cnt=(cnt+a[i]+vis[pr])%C;
	}
	for(int i=pos[L]+1; i<=pos[R]-1; i++) {//中间的进行二分查找
		cnt=(cnt+sum[i]+vis[i]*len);
	}
	return cnt%C;
}
int main() {
	cin>>n;
	len=sqrt(n);//分成的每个块的大小,一般长度都为sqrt(n)
	int num=ceil(n*1.0/len);//大概有多少块
	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<=n; i++) {
		cin>>a[i];
		pos[i]=(i-1)/len+1;//表示第i个数字在第几块里
		sum[pos[i]]+=a[i];
	}
	for(int i=1; i<=n; i++) {
		cin>>opt>>lll>>rr>>c;
		if(opt==0) {
			f(lll,rr,c);
		} else {
			cout<<ans(lll,rr,c+1)<<endl;
		}
	}
	return 0;
}

E . 数列分块入门 5

在这里插入图片描述

思路

区间开方+区间求和

  1. 用vis数组记录当前的块是否都是1,sum来记录这一块的和。修改的时候,如果是1的话,就不用再更新;如果不是,暴力一次,全开方,并对sum进行更新,再判断vis数组是否都是1,对v进行更新。

  2. 对于不完整块,进行暴力。将不完整块与完整块分开讨论。

Code:

/*

*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
#define ll long long
using namespace std;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
const ll  INFll = 0x3f3f3f3f3f3f3f3fLL;
const int INF= 0x7ffffff;
const int mod=998244353;
const int maxn =1e6+7;
int a[maxn];
int pos[maxn];
int sum[maxn];
int vis[maxn];
int n;
int len;
void f(int l,int r) {
	int pl=pos[l];
	int pr=pos[r];
	if(pl==pr) {
		for(int i=l; i<=r; i++) {
			sum[pl]-=a[i];
			a[i]=sqrt(a[i]);
			sum[pl]+=a[i];
		}
		return ;
	}
	for(int i=l; pos[i]==pl; i++) {//修改左端不完整块 更新sum值 
		sum[pl]-=a[i];
		a[i]=sqrt(a[i]);
		sum[pl]+=a[i];
	}
	for(int i=r; pos[i]==pr; i--) {//修改右端不完整块 更新sum值 
		sum[pr]-=a[i];
		a[i]=sqrt(a[i]);
		sum[pr]+=a[i];
	}
	for(int i=pl+1; i<=pr-1; i++) {
		if(vis[i]==0) {//作为标记 
			vis[i]=1;
			for(int j=(i-1)*len+1; pos[j]==i; j++) {
				sum[i]-=a[j];
				a[j]=sqrt(a[j]);
				sum[i]+=a[j];
				if(a[j]>1)
					vis[i]=0;
			}
		}
	}
}

int k(int l,int r) {
	int ans=0;
	int pl=pos[l];
	int pr=pos[r];
	if(pl==pr) {
		for(int i=l; i<=r; i++) {
			ans+=a[i];
		}
		return ans;

	}
	for(int i=l; pos[i]==pl; i++)
		ans+=a[i];
	for(int i=r; pos[i]==pr; i--)
		ans+=a[i];
	for(int i=pl+1; i<=pr-1; i++)
		ans+=sum[i];
	return ans;
}
int main() {
	cin>>n;
	len=sqrt(n);
	for(int i=1; i<=n; i++) {
		cin>>a[i];
		pos[i]=(i-1)/len+1;
		sum[pos[i]]+=a[i];
	}
	for(int i=1; i<=n; i++) {
		int opt,l,r,c;
		cin>>opt>>l>>r>>c;
		if(opt==0)
			f(l,r);
		else
			cout<<k(l,r)<<endl;
	}
	return 0;
}

B . 数列分块入门 6

在这里插入图片描述

思路

原链接
前几个题目都是用数组把所有数据都存放在一起。
此题用vector分别存放每个块的数据
当块内数据较大的时候,进行重新分块。

优点:耗时少。

Code:


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<stack>
#include<map>
#include<string>
#include<algorithm>
#include<sstream>
#include<memory>
#include<utility>
#include<functional>
#include<iterator>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxn=1e5+7;
int pos[maxn];
int vis[maxn];//每一块的长度 
int a[maxn*2];//用于重构的时候临时存放所有数据,因为有插入,所以数组扩大两倍 
int len,num;
int n;
int all;//数据总个数
vector<int>b[maxn];//b[i][j]表示元素在第i块中的第j个位置
void mer() {//分块 
	len=sqrt(n);//块的长度 
	num=ceil(n*1.0/len);//分块后新的块数 
	for(int i=1; i<=n; i++) {//第i个元素在第几块里 
		pos[i]=(i-1)/len+1;
		b[pos[i]].push_back(a[i]);
		vis[pos[i]]++;
	}
}
void div() {//进行重新分块 
	int cnt=1;
	for(int i=1; i<=num; i++) {//将已有的元素存储回a[i]
		int len=b[i].size();
		for(int j=0; j<len; j++) {
			a[cnt++]=b[i][j];
		}
		b[i].clear();
		vis[i]=0;
	}
}
void get(int l,int r) {
	int cnt=1;
	while(l>vis[cnt]) {
		l-=vis[cnt];
		cnt++;
	}
	b[cnt].insert(b[cnt].begin()+l-1,r);
	vis[cnt]++;
	all++;
	if(vis[cnt]>2*len) {//插入过多的话,进行重新分块 
		div();
		mer();
	}
}
void ins(int r) {
	int cnt=1;
	while(r>vis[cnt]) {
		r-=vis[cnt];
		cnt++;
	}
	cout<<b[cnt][r-1]<<endl;
}
int main() {
	cin>>n;
	for(int i=1; i<=n; i++) {
		cin>>a[i];
	}
	mer();
	for(int i=1; i<=n; i++) {
		int opt,l,r,c;
		cin>>opt>>l>>r>>c;
		if(opt==0)
			get(l,r);
		else
			ins(r);

	}
	return 0;
}


G . 数列分块入门 7

在这里插入图片描述在这里插入图片描述

思路

用两个数组的、分别对加法和乘法进行标记。
不完整的块:
a[i] = a[i] × mult[b[i]] + add[b[i]] ;
完整的块:

  1. 乘法:( a[i] × mult[b[i]] + add[b[i]] ) × c = a[i] × (mult[b[i]] × c) + (add[b[i]] × c) ,将mult和add都乘以c.
  2. 加法:( a[i] × mult[b[i]] + add[b[i]] ) + c = a[i] × mult[b[i]] + (add[b[i]] + c),将add加上c。

Code:

/*

*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
#define ll long long
using namespace std;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
const ll  INFll = 0x3f3f3f3f3f3f3f3fLL;
const int INF= 0x7ffffff;
const int mod = 1e4 + 7;
const int maxn = 1e5 + 10;

int a[maxn];
int add[maxn];
int mult[maxn];
int pos[maxn];
int n;
int len;

void init(int t) {
	for(int i = (t - 1) * len + 1; i <= t * len; i++) {
		a[i]=(a[i]*mult[t])%mod;
		a[i]=(a[i]+add[t])%mod;
	}
	mult[t] = 1;
	add[t] = 0;
}

void ad(int l, int r, int c) {
	init(pos[l]);
	for(int i = l; i <= r && i <= pos[l] * len; i++)
		a[i] = (a[i] + c) % mod;
	if(pos[l] != pos[r]) {
		init(pos[r]);
		for(int i = (pos[r] - 1) * len + 1; i <= r; i++)
			a[i] = (a[i] + c) % mod;
	}
	for(int i = pos[l] + 1; i <= pos[r] - 1; i++)
		add[i] = (add[i] + c) % mod;
}

void mul(int l, int r, int c) {
	init(pos[l]);
	for(int i = l; i <= r && i <= pos[l] * len; i++)
		a[i] = (a[i] * c) % mod;
	if(pos[l] != pos[r]) {
		init(pos[r]);
		for(int i = (pos[r] - 1) * len + 1; i <= r; i++)
			a[i] = (a[i] * c) % mod;
	}
	for(int i = pos[l] + 1; i <= pos[r] - 1; i++)
		mult[i] = (mult[i] * c) % mod, add[i] = (add[i] * c) % mod;
}

int qush(int t) {
	return (((a[t] * mult[pos[t]])) % mod + add[pos[t]]) % mod;
}

int main() {
	cin>>n;
	len = sqrt(n);
	for(int i = 1; i <= n; i++) {
		cin>>a[i];
		a[i]=a[i]%mod;
		pos[i] = (i - 1) / len+1;
		mult[i] = 1;
	}
	for(int i = 1; i <= n; i++) {

		int op , l , r , x ;
		cin>>op>>l>>r>>x;
		if(op==0) {
			ad(l, r, x);
		} else if(op==1) {
			mul(l, r, x);
		} else {
			cout<< qush(r)<<endl;
		}
	}
	return 0;
}

H . 数列分块入门 8

在这里插入图片描述

思路

用一个vis数组表示第i块是否都为一个数
b数组表示第i块变成的数

Code:

/*

*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
#define ll long long
using namespace std;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
const ll  INFll = 0x3f3f3f3f3f3f3f3fLL;
const int INF= 0x7ffffff;
const int mod = 1e4 + 7;
const int maxn = 1e5 + 10;

int n;
int pos[maxn];
int a[maxn],b[maxn];
int L[maxn],R[maxn];
int vis[maxn];
int len;
int l,r,c;

void f(int x) {
	if(vis[x]==0)
		return ;
	for(int i=L[x]; i<=R[x]; i++)
		a[i]=b[x];
	vis[x]=0;
	b[x]=-1;
}

int ans(int l,int r,int c) {
	int cnt=0;
	f(pos[l]);
	for(int i=l; i<=min(R[pos[l]],r); i++)
		if(a[i]==c)
			cnt++;
		else
			a[i]=c;
	if(pos[l]!=pos[r]) {
		f(pos[r]);
		for(int i=L[pos[r]]; i<=r; i++)
			if(a[i]==c)
				cnt++;
			else
				a[i]=c;
	}
	for(int i=pos[l]+1; i<=pos[r]-1; i++)
		if(vis[i]) {
			if(b[i]==c)
				cnt+=len;
			else
				b[i]=c;
		} else {
			for(int j=L[i]; j<=R[i]; j++) {
				if(a[j]==c)
					cnt++;

			}
			vis[i]=1;
			b[i]=c;
		}
	return cnt;
}
int main() {
	cin>>n;
	len=sqrt(n);
	for(int i=1; i<=n; i++)
		cin>>a[i];
	for(int i=1; i<=n; i++) {
		pos[i]=(i-1)/len+1;
		if(L[pos[i]]==0)
			L[pos[i]]=i;
		R[pos[i]]=i;
	}
	for(int i=1; i<=n; i++) {
		cin>>l>>r>>c;
		cout<<ans(l,r,c)<<endl;
	}
	return 0;
}

I . 数列分块入门 9

在这里插入图片描述

这道题看看大佬的题解吧

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值