zoukankan      html  css  js  c++  java
  • 数列分块入门 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
    ",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
    ",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
    ",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
    ",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
    ",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
    ",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
    ", 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
    ",modify(l,r,c));
    	}
    	return 0;
    }
    
  • 相关阅读:
    预处理与编译阶段
    联合体
    linux shell
    二维数组、字符数组、指针数组涉及字符串和具体元素问题
    二级指针的简单运用
    halcon算子翻译——get_image_type
    halcon算子翻译——get_image_time
    halcon算子翻译——get_image_size
    Halcon算子翻译——get_image_pointer3
    Halcon算子翻译——get_image_pointer1_rect
  • 原文地址:https://www.cnblogs.com/Vocanda/p/13519580.html
Copyright © 2011-2022 走看看