数列分块入门 1-8

数列分块入门 1-8(蒟蒻没写9)

数列分块入门 1

题目链接

题意是区间修改单点查询,运用分块思想,在区间里是一整块的直接打标记,零散的直接加,在查询的时候返回当前点的值加上它所属的块的加法标记即可

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 5e4+10;
int delta[maxn],a[maxn],bl[maxn];
int block;
void modify(int l,int r,int val){
	for(int i=l;i<=min(r,bl[l] * block);++i){
		a[i] += val;
	}
	if(bl[l] != bl[r]){
		for(int i=(bl[r] - 1) * block + 1;i <= r; ++i){
			a[i] += val;
		}
	}
	for(int i=bl[l] + 1;i <= bl[r] - 1;++i){
		delta[i] += val;
	}
}
int main(){
	int n;
	scanf("%d",&n);
	block = sqrt(n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		bl[i] = (i-1)/block + 1;
	}
	for(int i=1;i<=n;++i){
		int opt,l,r,val;
		scanf("%d%d%d%d",&opt,&l,&r,&val);
		if(opt){
			printf("%d\n",a[r] + delta[bl[r]]);
		}
		else{
			modify(l,r,val);
		}
	}
	return 0;
}

数列分块入门 2

题目链接

题意就是区间修改然后找区间内小于某个值的个数。

区间修改跟上一次的一样,打标记,因为要查询每一次比当前值小的有多少个,所以我们把每一块的值都放到一个 \(vector\) 中,并且排序,在对分散的点修改值完后,需要重新排序,查询的时候散点直接加上标记然后比较即可,整块的就记录一下要查的值减去当前块的标记,然后二分即可。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int maxn = 5e4+10;
vector<int> g[maxn];
int a[maxn],bl[maxn],delta[maxn];
int n;
int block;
void resort(int k){
	g[k].clear();
	for(int i = (k-1) * block + 1;i <= min(n,k * block);++i){
		g[k].push_back(a[i]);
	}
	sort(g[k].begin(),g[k].end());
}
void modify(int l,int r,int val){
	for(int i=l;i<=min(bl[l] * block,r);++i){
		a[i]+=val;
	}
	resort(bl[l]);
	if(bl[l] != bl[r]){
		for(int i = (bl[r]-1) * block + 1;i <= r;++i){
			a[i] += val;
		}
		resort(bl[r]);
	}
	for(int i=bl[l] + 1;i <= bl[r] - 1;++i){
		delta[i] += val;
	}
}
int query(int l,int r,int val){
	int ans = 0;
	for(int i=l;i<=min(bl[l] * block,r);++i){
		if(a[i]+delta[bl[l]] < val)ans++;
	}
	if(bl[l] != bl[r]){
		for(int i=(bl[r]-1) * block + 1;i<=r;++i){
			if(a[i] + delta[bl[r]] < val)ans++;
		}
	}
	for(int i=bl[l]+1;i<=bl[r]-1;++i){
		int x = val - delta[i];
		ans += lower_bound(g[i].begin(),g[i].end(),x) - g[i].begin();
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	block = sqrt(n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		bl[i] = (i-1)/block + 1;
		g[bl[i]].push_back(a[i]);
	}
	for(int i=1;i<=bl[n];++i){
		sort(g[i].begin(),g[i].end());
	}
	for(int i=1;i<=n;++i){
		int l,r,opt,val;
		scanf("%d%d%d%d",&opt,&l,&r,&val);
		if(opt == 0){
			modify(l,r,val);
		}
		else{
			printf("%d\n",query(l,r,val * val));
		}
	}
	return 0;
}

数列分块入门 3

题目链接

区间加法并查找前驱,跟上边一样用 \(vector\) 记录并排序,每次更新重新排序,在查询的时候散点就找最大的值加上标记,整块的就是二分,二分到的位置前一个就是。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int maxn = 1e5+10;
vector<int> g[maxn];
int a[maxn],bl[maxn],delta[maxn];
int n;
int block;
void resort(int k){
	g[k].clear();
	for(int i = (k-1) * block + 1;i <= min(n,k * block);++i){
		g[k].push_back(a[i]);
	}
	sort(g[k].begin(),g[k].end());
}
void modify(int l,int r,int val){
	for(int i=l;i<=min(bl[l] * block,r);++i){
		a[i]+=val;
	}
	resort(bl[l]);
	if(bl[l] != bl[r]){
		for(int i = (bl[r]-1) * block + 1;i <= r;++i){
			a[i] += val;
		}
		resort(bl[r]);
	}
	for(int i=bl[l] + 1;i <= bl[r] - 1;++i){
		delta[i] += val;
	}
}
int query(int l,int r,int val){
	int ans = -1;
	for(int i=l;i<=min(bl[l] * block,r);++i){
		if(a[i]+delta[bl[l]] < val){
			ans = max(ans,a[i]+delta[bl[l]]);
		}
	}
	if(bl[l] != bl[r]){
		for(int i=(bl[r]-1) * block + 1;i<=r;++i){
			if(a[i] + delta[bl[r]] < val){
				ans = max(ans,a[i] + delta[bl[r]]);
			}
		}
	}
	for(int i=bl[l]+1;i<=bl[r]-1;++i){
		int x = lower_bound(g[i].begin(),g[i].end(),val - delta[i]) - g[i].begin();
		if(x){
			ans = max(ans,g[i][x-1] + delta[i]);
		}
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	block = sqrt(n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		bl[i] = (i-1)/block + 1;
		g[bl[i]].push_back(a[i]);
	}
	for(int i=1;i<=bl[n];++i){
		sort(g[i].begin(),g[i].end());
	}
	for(int i=1;i<=n;++i){
		int l,r,opt,val;
		scanf("%d%d%d%d",&opt,&l,&r,&val);
		if(opt == 0){
			modify(l,r,val);
		}
		else{
			printf("%d\n",query(l,r,val));
		}
	}
	return 0;
}

数列分块入门 4

题目链接

区间查询并区间修改。

区间修改跟上边大同小异,但是需要记录每一个块加上了多少(包括散点),在查询的时候散点正常加上标记,整块的就加上记录下来的那个每一块加上了多少即可。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
#define int long long
const int maxn = 5e4+10;
int a[maxn],bl[maxn],delta[maxn];
int n;
int jl[maxn];
int block;
void modify(int l,int r,int val){
	for(int i=l;i<=min(bl[l] * block,r);++i){
		a[i]+=val;
		jl[bl[l]] += val;
	}
	if(bl[l] != bl[r]){
		for(int i = (bl[r]-1) * block + 1;i <= r;++i){
			a[i] += val;
			jl[bl[r]] += val;
		}
	}
	for(int i=bl[l] + 1;i <= bl[r] - 1;++i){
		delta[i] += val;
		jl[i] += val * block;
	}
}
int query(int l,int r,int mod){
	int ans = 0;
	mod++;
	for(int i=l;i<=min(bl[l] * block,r);++i){
		ans += (a[i] + delta[bl[l]]) % mod;
		ans %= mod;
	}
	if(bl[l] != bl[r]){
		for(int i = (bl[r]-1) * block + 1;i <= r;++i){
			ans += (a[i] + delta[bl[r]]) % mod;
			ans %= mod;
		}
	}
	for(int i=bl[l] + 1;i <= bl[r] - 1;++i){
		ans = (ans + jl[i]) % mod;
	}
	return ans % mod;
}
signed main(){
	scanf("%lld",&n);
	block = sqrt(n);
	for(int i=1;i<=n;++i){
		scanf("%lld",&a[i]);
		bl[i] = (i-1)/block + 1;
		jl[bl[i]] += a[i];
	}
	for(int i=1;i<=n;++i){
		int l,r,opt,val;
		scanf("%lld%lld%lld%lld",&opt,&l,&r,&val);
		if(opt == 0){
			modify(l,r,val);
		}
		else{
			printf("%lld\n",query(l,r,val));
		}
	}
	return 0;
}

数列分块入门 5

题目链接

区间开方 + 区间求和。

我们用一个记录数组记录每个块的值,在进行散点的修改时先减去当前点值,开根号后再加上。

需要注意的是,当一个数是 \(1\)\(0\) 的时候就不用再开方了,所以在整块修改的时候我们先置空当前块的记录值,然后依次加上开根后的值,判断一下是否有 \(0\)\(1\) ,如果没有就标记为 \(0\) ,当标记为 \(1\) 时,当前块不需要开根,查询的时候单点单加,整块的就直接加上记录的值即可。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
#define int long long
const int maxn = 5e4+10;
int a[maxn],bl[maxn],delta[maxn];
int n;
int jl[maxn];
bool flag[maxn];
int block;
void change(int id){
	jl[id] = 0;
	bool jud = 1;
	for(int i=(id-1) * block + 1; i<=id * block; ++i){
		a[i] = sqrt(a[i]);
		jl[id] += a[i];
		if(a[i] != 1 && a[i] != 0){
			jud = 0;
		}
	}
	flag[id] = jud;
}
void modify(int l,int r){
	for(int i=l;i<=min(bl[l] * block,r);++i){
		jl[bl[l]] -= a[i];
		a[i] = sqrt(a[i]);
		jl[bl[l]] += a[i];
	}
	if(bl[l] != bl[r]){
		for(int i = (bl[r]-1) * block + 1;i <= r;++i){
			jl[bl[r]] -= a[i];
			a[i] = sqrt(a[i]);
			jl[bl[r]] += a[i];
		}
	}
	for(int i=bl[l] + 1;i <= bl[r] - 1;++i){
		if(flag[i])continue;
		change(i);
	}
}
int query(int l,int r){
	int ans = 0;
	for(int i=l;i<=min(bl[l] * block,r);++i){
		ans += a[i];
	}
	if(bl[l] != bl[r]){
		for(int i = (bl[r]-1) * block + 1;i <= r;++i){
			ans += a[i];
		}
	}
	for(int i=bl[l] + 1;i <= bl[r] - 1;++i){
		ans += jl[i];
	}
	return ans;
}
signed main(){
	scanf("%lld",&n);
	block = sqrt(n);
	for(int i=1;i<=n;++i){
		scanf("%lld",&a[i]);
		bl[i] = (i-1)/block + 1;
		jl[bl[i]] += a[i];
	}
	for(int i=1;i<=n;++i){
		int l,r,opt,val;
		scanf("%lld%lld%lld%lld",&opt,&l,&r,&val);
		if(opt == 0){
			modify(l,r);
		}
		else{
			printf("%lld\n",query(l,r));
		}
	}
	return 0;
}

数列分块入门 6

题目链接

单点插入,单点询问。

看到插入我们很自然的应该就能想到用 \(vector\) ,因为他的插入操作是最便捷的。

插入的时候依次枚举块,逐渐把要插入的位置减去块的大小,最终就是要插入的位置,直接插入。

查询的时候跟插入一样,依次减去块大小,最后找到 \(pos-1\) 的值就行。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int L=1<<20;
char buffer[L],*S,*T;
#define lowbit(x) (x & -x)
#define getchar() (S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T)?EOF:*S++)
#define inline __inline__ __attribute__((__always_inline__))
#define max(a,b) (a>b?a:b)
#define re register 
const int maxn = 1e5+10;
struct Node{
	int x,y,val;
}e[maxn+200000];
int bl[maxn],block;
vector<int>g[maxn];
inline int read(){
	int s = 0,f = 1;
	char ch = getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))s = s * 10 + ch - '0',ch = getchar();
	return s * f;
}
inline void insert(int pos,int val){
	int now = 1;
	while(pos > g[now].size()){
		pos -= g[now].size();
		now++;
	}
	g[now].insert(g[now].begin()+pos,val);
}
inline int query(int pos){
	int now = 1;
	while(pos > g[now].size()){
		pos -= g[now].size();
		now++;
	}
	return g[now][pos-1];
}
int main(){
	re int n = read();
	block = sqrt(n);
	for(re int i=1;i<=n;++i){
		bl[i] = (i-1)/block + 1;
		re int x = read();
		g[bl[i]].push_back(x);
	}
	for(re int i = 1;i<=n;++i){
		int opt = read(),l = read(),r = read(),val = read();
		if(opt == 0){
			insert(l-1,r);
		}
		else{
			printf("%d\n",query(r));
		}
	}
	return 0;
}

数列分块入门 7

题目链接

区间乘法 + 加法 + 单点查询。

其实跟其他的没什么区别,只是加一个乘法标记,在每一次修改的时候都要标记下放,查询的时候先乘后加即可。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<vector>
using namespace std;
const int L=1<<20;
char buffer[L],*S,*T;
#define getchar() (S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T)?EOF:*S++)
#define inline __inline__ __attribute__((__always_inline__))
#define re register 
#define int long long
const int maxn = 1e6+10;
const int mod = 1e4+7;
struct Node{
	int x,y,val;
}e[maxn];
int a[maxn];
int bl[maxn],block;
int add[maxn],mul[maxn];
int n;
inline int read(){
	int s = 0,f = 1;
	char ch = getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))s = s * 10 + ch - '0',ch = getchar();
	return s * f;
}
inline void pushdown(int id){
	for(int i = (id - 1) * block + 1;i <= id * block;++i){
		a[i] = (a[i] * mul[bl[i]] % mod + add[bl[i]] % mod) % mod;
	}
	add[id] = 0;
	mul[id] = 1;
}
inline void modify(int l,int r,int val){
	pushdown(bl[l]);
	for(int i = l;i <= min(bl[l] * block,r);++i){
		a[i] = (a[i] + val) % mod;
	}
	if(bl[l] != bl[r]){
		pushdown(bl[r]);
		for(int i = (bl[r] - 1) * block + 1 ;i <= r;++i){
			a[i] = (a[i] + val) % mod;
		}
	}
	for(int i=bl[l] + 1;i <= bl[r] - 1;++i){
		add[i] = (add[i] + val) % mod;
	}
}
inline void multiply(int l,int r,int val){
	pushdown(bl[l]);
	for(int i = l;i <= min(bl[l] * block,r);++i){
		a[i] = (a[i] * val) % mod;
	}
	if(bl[l] != bl[r]){
		pushdown(bl[r]);
		for(int i = (bl[r] - 1) * block + 1; i <= r;++i){
			a[i] = (a[i] * val) % mod;
		}
	}
	for(int i=bl[l] + 1;i <= bl[r] - 1;++i){
		add[i] = (add[i] * val) % mod;
		mul[i] = (mul[i] * val) % mod;
	}
}
inline int query(int pos){
	return (a[pos] * mul[bl[pos]] % mod + add[bl[pos]] % mod) % mod;
}
signed main(){
	n = read();
	block = sqrt(n);
	for(int i=1;i<=n;++i){
		a[i] = read();
		bl[i] = (i-1)/block + 1;
		mul[bl[i]] = 1;
	}
	for (int i = 1; i <= n; ++i) {
		int t, x, y, z;
		t = read(),x = read(),y = read(),z = read();
		if (t == 0) {
			modify(x, y, z);
		} else if (t == 1) {
			multiply(x, y, z);
		} else {
			printf("%lld\n", query(y));
		}
	}
	return 0;
}

数列分块入门 8

题目链接

询问等于一个数 \(c\) 的元素,并将这个区间的所有元素改为 \(c\)

散点直接修改并查询,设置一个 \(tag\) 标记,先下放,整块修改的话,如果当前的标记有值,且不等于 \(c\) ,那么直接给标记赋值,统计答案。

如果标记没值,那么扫一遍当前块,直接赋值(同散点),最后标记记录 \(c\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<vector>
using namespace std;
const int L=1<<20;
char buffer[L],*S,*T;
#define getchar() (S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T)?EOF:*S++)
#define inline __inline__ __attribute__((__always_inline__))
#define re register 
const int maxn = 1e6+10;
int tag[maxn],a[maxn],bl[maxn],block;
int n;
inline int read(){
	int s = 0,f = 1;
	char ch = getchar();
	while(!isdigit(ch)){
		if(ch == '-')f = -1;
		ch = getchar();
	}
	while(isdigit(ch)){
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * f;
}
inline void pushdown(int id){
	if(tag[id] == -1)return;
	for(re int i = (id - 1) * block + 1; i <= id * block; ++i){
		a[i] = tag[id];
	}
	tag[id] = -1;
}
inline int modify(int l,int r,int c){
	pushdown(bl[l]);
	re int ans = 0;
	for(re int i = l;i <= min(bl[l] * block,r);++i){
		if(a[i] != c){
			a[i] = c;
		}
		else ans++;
	}
	if(bl[l] != bl[r]){
		pushdown(bl[r]);
		for(re int i = (bl[r] - 1) * block + 1; i <= r;++i){
			if(a[i] != c){
				a[i] = c;
			}
			else ans++;
		}
	}
	for(re int i = bl[l] + 1;i <= bl[r] - 1;++i){
		if(tag[i] != -1){
			if(tag[i] != c){
				tag[i] = c;
			}
			else ans += block;
		}
		else{
			for(re int j = (i-1) * block + 1;j <= i * block; ++j){
				if(a[j] != c){
					a[j] = c;
				}
				else ans++;
			}
			tag[i] = c;
		}
	}
	return ans;
}
int main(){
	memset(tag,-1,sizeof(tag));
	n = read();
	block = sqrt(n);
	for(re int i = 1;i<=n;++i){
		a[i] = read();
		bl[i] = (i-1)/block + 1;
	}
	for(re int i = 1;i <= n;++i){
		re int l = read(),r = read(),c = read();
		printf("%d\n",modify(l,r,c));
	}
	return 0;
}
posted @ 2020-08-17 19:40  Vocanda  阅读(315)  评论(5编辑  收藏  举报