首页 > 其他分享 >序列分块入门

序列分块入门

时间:2022-12-27 17:57:37浏览次数:47  
标签:入门 分块 int sqrt BL blk 序列 op

本文不涉及 Ynoi 大分块

分块简介

对于一道数据结构题,如果常规思路(如线段树、BIT、ST 表、主席树、平衡树、树套树、K-D Tree 等)不好做,我们可以考虑将序列分成 \(B\) 块,进行区间 修改/询问 的时候散块暴力,整块通过一些奇妙的操作做到更优的复杂度。

\(B\) 一般取 \(\sqrt{n}\) 或者 \(\sqrt{n\log n}\)。

这里给出将序列 \(a\) 分成 \(\texttt{blk}\) 块。第 \(i\) 个块是 \([\texttt{bl}_i,\texttt{br}_i]\)。\(a_i\) 归属块 \(\texttt{bel}_i\)。第 \(i\) 个块有 \(\texttt{bs}_i\) 个元素(即块长)。

blk=分块个数
for(int i=1;i<=blk;i++){bl[i]=n/blk*(i-1)+1;br[i]=n/blk*i;}
br[blk]=n;
for(int i=1;i<=blk;i++){
	for(int j=bl[i];j<=br[i];j++){bel[j]=i;bs[i]++;}
}

约定几个名词:

  • 整块:指区间中间的几个完整的块。
  • 散块:指区间左右端的非完整块。
  • 标记:又称懒标记(Lazy Tag),指修改不方便时,通过一个标记来保存修改状态,等到合适的实际和值进行合并。一般用于线段树中。

例题选讲

洛谷P3374 【模板】树状数组 1

给你一个长为 \(n\) 的序列,有 \(m\) 个操作,支持:

  • 1 x k 将第 \(x\) 个元素加上 \(k\)。
  • 2 x y 输出区间 \([x,y]\) 的和。

\(1 \leq n,m \leq 5\times10^5\)。

首先对原序列进行分块。我们在维护序列的同时维护每个块的块内和。

对于 1 操作,我们更新原序列和它所属的块的块内和。时间复杂度 \(O(1)\)。

对于 2 操作,如果区间在一个块内我们就直接暴力搞,否则我们把区间分成三段:

  • 左边的散块 \([l,\texttt{br}[\texttt{bel}[l]]\) 和右边的散块 \([\texttt{bl}[{\texttt{bel}[r]}],r]\) 我们直接暴力搞。
  • 中间的整块,由于是几个完整的块,我们直接将所有块的块内和累加。

时间复杂度 \(O(\frac{n}{B}+B)\),取 \(B=\sqrt{n}\) 最优,为 \(O(\sqrt{n})\)。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 5e5+5,BL = 5e3+5;
int a[N],bl[BL],br[BL],bel[N],bs[BL],val[BL];
int n,blk,m;

inline void preworks(){
	blk=sqrt(n);
	for(int i=1;i<=blk;i++){bl[i]=n/blk*(i-1)+1;br[i]=n/blk*i;}
	br[blk]=n;
	for(int i=1;i<=blk;i++){
		for(int j=bl[i];j<=br[i];j++){bel[j]=i;bs[i]++;}
	}
	for(int i=1;i<=n;i++) val[bel[i]]+=a[i];
}

inline void update(int p,int v){
	a[p]+=v;
	val[bel[p]]+=v;
}

inline int query(int l,int r){
	if(bel[l]==bel[r]){
		return accumulate(a+l,a+r+1,0ll);
	}
	return accumulate(a+l,a+br[bel[l]]+1,0ll)+accumulate(a+bl[bel[r]],a+r+1,0ll)+accumulate(val+bel[l]+1,val+bel[r],0ll);
}

signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	preworks();
	while(m--){
		int op,x,y;
		cin>>op>>x>>y;
		if(op==1) update(x,y);
		else cout<<query(x,y)<<'\n';
	}
	return 0;
}

洛谷P3368 【模板】树状数组 2

给你一个长为 \(n\) 的序列,有 \(m\) 个操作,支持:

  • 1 x y k 将位于区间 \([x,y]\) 的元素加上 \(k\)。
  • 2 x 输出第 \(x\) 个元素的值。

\(1 \leq n,m \leq 5\times10^5\)。

这道题可以在上一道题的分块加上差分完成。但那就没有达到练习的目的了。

区间修改如果维护块内和的话散块和查询都不好搞,我们考虑引入线段树思想。给每一个块上打一个加法标记。修改的时候整块直接打标记,散块暴力改。单点查询的时候将原序列的值加上所在块的标记值即可。

查询时间复杂度 \(O(1)\) ,修改时间复杂度 \(O(\frac{n}{B}+B)\),取 \(B=\sqrt{n}\) 最优,为 \(O(\sqrt{n})\)。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 5e5+5,BL = 5e3+5;
int a[N],bl[BL],br[BL],bel[N],bs[BL],tag[BL];
int n,blk,m;

inline void preworks(){
	blk=sqrt(n);
	for(int i=1;i<=blk;i++){bl[i]=n/blk*(i-1)+1;br[i]=n/blk*i;}
	br[blk]=n;
	for(int i=1;i<=blk;i++){
		for(int j=bl[i];j<=br[i];j++){bel[j]=i;bs[i]++;}
	}
}

inline void update(int l,int r,int v){
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++) a[i]+=v;
		return;
	}
	for(int i=l;i<=br[bel[l]];i++) a[i]+=v;
	for(int i=bel[l]+1;i<bel[r];i++) tag[i]+=v;
	for(int i=bl[bel[r]];i<=r;i++) a[i]+=v;
}

inline int query(int p){
	return a[p]+tag[bel[p]];
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	preworks();
	while(m--){
		int op,l,r,v;
		cin>>op>>l;
		if(op==1) {cin>>r>>v;update(l,r,v);}
		else cout<<query(l)<<'\n';
	}
	return 0;
}

洛谷P1908 逆序对

对于位于 \([1,n]\) 的两个数 \(i,j\)。如果 \(i>j\) 且 \(a_i<a_j\),则称有序数对 \((i,j)\) 是一对逆序对。

给出一个长为 \(n\) 的序列 \(a\)。输出这个序列中有多少对逆序对。

\(1 \leq n \leq 5\times10^5,1\leq a_i \leq 10^9\)

逆序对是一个经典二维偏序问题。一般用树状数组解决。而树状数组能做的,分块当然可以做!

先离散化(貌似可以不离散化,不过我不会)

然后对值域分块,遍历序列,到 \(a_i\) 时将 \(a_i\) 插进值域分块,然后查询 \(i-\sum_{1}^{a_i}\)(即序号比它小,值比它大的元素个数)。累加即可。实现这个功能,只需要实现单点修改区间查询。

时间复杂度 \(O(n\sqrt{n})\)。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5+5,BL = 5e3+5;
int a[N],bl[BL],br[BL],bel[N],bs[BL],val[BL];
int n,blk,m;
inline void preworks(){
	blk=sqrt(n);
	for(int i=1;i<=blk;i++){bl[i]=n/blk*(i-1)+1;br[i]=n/blk*i;}
	br[blk]=n;
	for(int i=1;i<=blk;i++){
		for(int j=bl[i];j<=br[i];j++){bel[j]=i;bs[i]++;}
	}
}
inline void update(int p,int v){
	a[p]+=v;
	val[bel[p]]+=v;
}
inline int query(int l,int r){
	if(bel[l]==bel[r]){
		return accumulate(a+l,a+r+1,0ll);
	}
	return accumulate(a+l,a+br[bel[l]]+1,0ll)+accumulate(a+bl[bel[r]],a+r+1,0ll)+accumulate(val+bel[l]+1,val+bel[r],0ll);
}
int rk[N];
struct node{
	int v,id;
	bool operator<(const node x) const {
		return v<x.v;
	}
} v[N];
int ans;
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){cin>>v[i].v;v[i].id=i;}
	stable_sort(v+1,v+n+1);
	for(int i=1;i<=n;i++) rk[v[i].id]=i;
	preworks();
	for(int i=1;i<=n;i++){update(rk[i],1);ans += (i-query(1,rk[i]));}
	cout<<ans;
	return 0;
}

洛谷P3372 【模板】线段树 1

给你一个长为 \(n\) 的序列,有 \(m\) 个操作,支持:

  • 1 x y k 将位于区间 \([x,y]\) 的元素加上 \(k\)。
  • 2 x y 输出 \([x,y]\) 的和。

\(1 \leq n,m \leq10^5\)。

这道题是区间修改区间查询。我们考虑将第一题和第二题的思路结合起来。既维护块内和,也维护加法标记。

对于修改操作,如果在同一块仍然暴力修改,否则对于左右散块暴力改,整块打标记。

对于查询操作,如果在同一块就暴力查询,否则左右散块暴力求和,整块直接用块内和加上标记乘上块长即可。记得暴力求和时需要加上标记。

修改和查询都是 \(O(\frac{n}{B}+B)\) 的,取 \(B=\sqrt{n}\) 最优,为 \(O(\sqrt{n})\)。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e5+5,BL = 1e3+5;
int a[N],bl[BL],br[BL],bel[N],bs[BL],val[BL],tag[BL];
int n,blk,m;

inline void preworks(){
	blk=sqrt(n);
	for(int i=1;i<=blk;i++){bl[i]=n/blk*(i-1)+1;br[i]=n/blk*i;}
	br[blk]=n;
	for(int i=1;i<=blk;i++){
		for(int j=bl[i];j<=br[i];j++){bel[j]=i;bs[i]++;}
	}
	for(int i=1;i<=n;i++) val[bel[i]]+=a[i];
}

void update(int l,int r,int v){
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++){a[i]+=v;val[bel[i]]+=v;}
		return;
	}
	for(int i=l;i<=br[bel[l]];i++){a[i]+=v;val[bel[i]]+=v;}
	for(int i=bel[l]+1;i<bel[r];i++){tag[i]+=v;}
	for(int i=bl[bel[r]];i<=r;i++){a[i]+=v;val[bel[i]]+=v;}
}

int query(int l,int r){
	int ret=0;
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++) ret+=(a[i]+tag[bel[i]]);
		return ret;
	}
	for(int i=l;i<=br[bel[l]];i++) ret+=(a[i]+tag[bel[i]]);
	for(int i=bel[l]+1;i<bel[r];i++) ret+=(tag[i]*bs[i]+val[i]);
	for(int i=bl[bel[r]];i<=r;i++) ret+=(a[i]+tag[bel[i]]);
	return ret;
}

signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	preworks();
	while(m--){
		int op,x,y,v;
		cin>>op>>x>>y;
		if(op==1){cin>>v;update(x,y,v);}
		else cout<<query(x,y)<<'\n';
	}
	return 0;
}

LibreOJ6278 数列分块入门 2

给你一个长为 \(n\) 的序列,有 \(n\) 个操作,支持:

  • 0 l r c 将位于区间 \([l,r]\) 的元素加上 \(c\)。
  • 1 l r c 输出 \([l,r]\) 中,有多少个元素小于 \(c^2\)。

\(1\leq n\leq5\times10^4\)。

先考虑静态全局版本,如何查询一个序列有多少个元素小于 \(c^2\)?最朴素的思路是挨个找,但是这样子单次询问需要 \(O(n)\) 的时间。我们可以将其排序一遍后,每一次查询只需要二分即可。就可以做到预处理 \(O(n\log n)\),单次询问 \(O(\log n)\)。应该是最优的了。

然后考虑如何查询区间有多少个元素小于 \(c^2\)?我们将原序列分块,然后询问就可以看成散块暴力,整块执行我们刚才讨论的静态全局版本。就可以做到预处理 \(O(n\log B)\),单次询问 \(O(B+\frac{n}{B}\log B)\)。

然后加上修改。同样使用打加法标记法。容易发现整块同时加上一个数,块内排序后顺序不变。因此每次修改只需要将散块所在的两个块重新排序即可。单次修改时间复杂度 \(O(B\log B+\frac{n}{B})\)。

貌似 \(B=\sqrt{n}\) 仍然最优,预处理 \(O(n\log\sqrt{n})\)。单次操作为 \(O(\sqrt{n}\log\sqrt{n})=O(\sqrt{n}\log n)\)。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e6+5,BL = 1e3+5;
int blk,bl[BL],br[BL],bs[BL],bel[N],a[N],add[BL];
vector<int> sorted[BL];
int n,q;

inline void sort(int p){
	sorted[p].clear();
	for(int i=bl[p];i<=br[p];i++) sorted[p].push_back(a[i]);
	stable_sort(sorted[p].begin(),sorted[p].end());
}

inline void preworks(){
	blk=sqrt(n);
	for(int i=1;i<=blk;i++){bl[i]=n/blk*(i-1)+1;br[i]=n/blk*i;}
	br[blk]=n;
	for(int i=1;i<=blk;i++){
		for(int j=bl[i];j<=br[i];j++){bel[j]=i;bs[i]++;}
	}
	for(int i=1;i<=blk;i++) sort(i);
}

inline void update(int l,int r,int v){
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++) a[i]+=v;
		sort(bel[l]);return;
	}
	for(int i=l;i<=br[bel[l]];i++) a[i]+=v;
	sort(bel[l]);
	for(int i=bl[bel[r]];i<=r;i++) a[i]+=v;
	sort(bel[r]);
	for(int i=bel[l]+1;i<bel[r];i++) add[i]+=v;
}

inline int query(int l,int r,int c){
	c=c*c;
	int ans=0;
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++) ans+=(a[i]+add[bel[i]]<c);
		return ans;
	}
	for(int i=l;i<=br[bel[l]];i++) ans+=(a[i]+add[bel[i]]<c);
	for(int i=bl[bel[r]];i<=r;i++) ans+=(a[i]+add[bel[i]]<c);
	for(int i=bel[l]+1;i<bel[r];i++){
		ans+=(lower_bound(sorted[i].begin(),sorted[i].end(),c-add[i])-sorted[i].begin());
	}
	return ans;
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n;
	q=n;
	for(int i=1;i<=n;i++) cin>>a[i];
	preworks();
	while(q--){
		int l,r,v;char op;
		cin>>op>>l>>r>>v;
		if(op=='0') update(l,r,v);
		else cout<<query(l,r,v)<<'\n';
	}
	return 0;
}

LibreOJ6279 数列分块入门 3

给你一个长为 \(n\) 的序列,有 \(n\) 个操作,支持:

  • 0 l r c 将位于区间 \([l,r]\) 的元素加上 \(c\)。
  • 1 l r c 输出 \([l,r]\) 中 \(c\) 的前驱。

\(1\leq n\leq10^5\)。

注:一个数在一个序列中的前驱 就是序列中 最大的 比这个数小 的数。

和上一题差不多,也是预处理时将整块排序。修改时打标记+散块暴力排序。查询时散块暴力,整块的时候二分,如果没有找到比 \(c\) 小的,否则就找到最大的比 \(c\) 小的。最后将求得的值取最大值即可。

时间复杂度和上一题一样。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e6+5,BL = 1e3+5;
int blk,bl[BL],br[BL],bs[BL],bel[N],a[N],add[BL];
vector<int> sorted[BL];
int n,q;

inline void sort(int p){
	sorted[p].clear();
	for(int i=bl[p];i<=br[p];i++) sorted[p].push_back(a[i]);
	stable_sort(sorted[p].begin(),sorted[p].end());
}

inline void preworks(){
	blk=sqrt(n);
	for(int i=1;i<=blk;i++){bl[i]=n/blk*(i-1)+1;br[i]=n/blk*i;}
	br[blk]=n;
	for(int i=1;i<=blk;i++){
		for(int j=bl[i];j<=br[i];j++){bel[j]=i;bs[i]++;}
	}
	for(int i=1;i<=blk;i++) sort(i);
}

inline void update(int l,int r,int v){
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++) a[i]+=v;
		sort(bel[l]);return;
	}
	for(int i=l;i<=br[bel[l]];i++) a[i]+=v;
	sort(bel[l]);
	for(int i=bl[bel[r]];i<=r;i++) a[i]+=v;
	sort(bel[r]);
	for(int i=bel[l]+1;i<bel[r];i++) add[i]+=v;
}

inline int query(int l,int r,int c){
	int ans=-1;
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++) {
			if(a[i]+add[bel[i]]<c) ans=max(ans,a[i]+add[bel[i]]);
		}
		return ans;
	}
	for(int i=l;i<=br[bel[l]];i++) {
		if(a[i]+add[bel[i]]<c) ans=max(ans,a[i]+add[bel[i]]);
	}
	for(int i=bl[bel[r]];i<=r;i++) {
		if(a[i]+add[bel[i]]<c) ans=max(ans,a[i]+add[bel[i]]);
	}
	for(int i=bel[l]+1;i<bel[r];i++){
		int v=lower_bound(sorted[i].begin(),sorted[i].end(),c-add[i])-sorted[i].begin();
		if(v == 0) continue;
		if(sorted[i][v-1]==(c-add[i])) v--;
		v = sorted[i][v-1];
		ans=max(ans,v+add[i]);
	}
	return ans;
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n;
	q=n;
	for(int i=1;i<=n;i++) cin>>a[i];
	preworks();
	while(q--){
		int l,r,v;char op;
		cin>>op>>l>>r>>v;
		if(op=='0') update(l,r,v);
		else cout<<query(l,r,v)<<'\n';
	}
	return 0;
}

LibreOJ6281 数列分块入门 5

给你一个长为 \(n\) 的自然数序列,有 \(n\) 个操作,支持:

  • 0 l r c 将位于区间 \([l,r]\) 的元素取平方根并向下取整。
  • 1 l r c 输出 \([l,r]\) 的和。

\(1 \leq n \leq 5\times10^4\)

平方根有一个性质,任意一个自然数在经过一定次数的开方向下取整后,一定是一个小于等于 \(1\) 的自然数。

于是修改操作,我们散块暴力,整块先判断这个块的所有元素是否小于等于 \(1\)(这个可以维护)。如果是就不必暴力了,如果不是就暴力开根。

查询操作就照例维护块内和,散块暴力整块累加块内和即可。

时间复杂度不会算,不过可以通过本题。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 5e5+5,BL = 5e3+5;
int a[N],bl[BL],br[BL],bel[N],bs[BL],val[BL];
bool one[N];
int n,blk;

inline void preworks(){
	blk=sqrt(n);
	for(int i=1;i<=blk;i++){bl[i]=n/blk*(i-1)+1;br[i]=n/blk*i;}
	br[blk]=n;
	for(int i=1;i<=blk;i++){
		for(int j=bl[i];j<=br[i];j++){bel[j]=i;bs[i]++;}
	}
	for(int i=1;i<=n;i++) val[bel[i]]+=a[i];
}

inline void update(int l,int r){
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++){
			val[bel[i]]-=a[i];
			a[i]=sqrt(a[i]);
			val[bel[i]]+=a[i];
		}
		return;
	}
	for(int i=l;i<=br[bel[l]];i++){
		val[bel[i]]-=a[i];
		a[i]=sqrt(a[i]);
		val[bel[i]]+=a[i];
	}
	for(int i=bel[l]+1;i<bel[r];i++){
		if(one[i]) continue;
		one[i]=true;
		for(int j=bl[i];j<=br[i];j++){
			val[i]-=a[j];
			a[j]=sqrt(a[j]);
			val[i]+=a[j];
			if(a[j] > 1) one[i]=false;
		}
	}
	for(int i=bl[bel[r]];i<=r;i++){
		val[bel[i]]-=a[i];
		a[i]=sqrt(a[i]);
		val[bel[i]]+=a[i];
	}
}

inline int query(int l,int r){
	if(bel[l]==bel[r]){
		return accumulate(a+l,a+r+1,0ll);
	}
	return accumulate(a+l,a+br[bel[l]]+1,0ll)+accumulate(a+bl[bel[r]],a+r+1,0ll)+accumulate(val+bel[l]+1,val+bel[r],0ll);
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	preworks();
	for(int i=1;i<=n;i++){
		int op,l,r,c;
		cin>>op>>l>>r>>c;
		if(op==0) update(l,r);
		else cout<<query(l,r)<<'\n';
	}
	return 0;
}

LibreOJ6282 数列分块入门 6

给你一个长为 \(n\) 的自然数序列,有 \(n\) 个操作,支持:

  • 0 l r c 将 \(r\) 插入到第 \(l\) 个元素前。
  • 1 l r c 输出第 \(r\) 个元素的值。

\(1 \leq n \leq 10^5\)

这道题如果直接用 vector 复杂度是 \(O(n^2)\) 的,难以通过本题(实际上无压力跑过)

我们考虑将序列分块,每个块用一个 vector 来维护。插入的时候找到对应块的 vector 插入。但是这样子如果不停地往一个块插入就直接退化成 \(O(n^2)\) 了。我们可以定义一个阈值 \(\alpha\),在每次插入后判断如果当前块长大于预期块长的 \(\alpha\) 倍,就重构整个序列。询问就直接找到对应的块即可。

\(\alpha\) 取 \(7\) 最优。

时间复杂度期望 \(O(n\sqrt{n})\)。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int BL = 500;
const int alpha=7;

vector<int> arr[BL];
int n,blk;

void build(){
	vector<int> a;
	a.push_back(0);
	for(int i=1;i<=blk;i++){
		for(int j:arr[i]) a.push_back(j);
		arr[i].clear();
	}
	blk=sqrt(n);
	vector<int> bl(blk+1),br(blk+1);
	for(int i=1;i<=blk;i++){bl[i]=n/blk*(i-1)+1;br[i]=n/blk*i;}
	br[blk]=n;
	for(int i=1;i<=blk;i++){
		for(int j=bl[i];j<=br[i];j++) arr[i].push_back(a[j]);
	}
}

pair<int,int> getpos(int p){
	int vec=1,cursor=0;
	for(;cursor+arr[vec].size()<p;cursor+=arr[vec].size(),vec++);
	return make_pair(vec,p-cursor-1);
}

inline void insert(int p,int v){
	pair<int,int> ppos=getpos(p);
	arr[ppos.first].insert(arr[ppos.first].begin()+getpos(p).second,v);
	n++;
	if(arr[ppos.first].size()>(alpha*(n/blk))) build();
}

inline int query(int p){
	pair<int,int> ppos=getpos(p);
	return arr[ppos.first][ppos.second];
}

signed main(){
	cin>>n;
	blk=1;
	for(int i=1;i<=n;i++){
		int v;cin>>v;
		arr[blk].push_back(v);
	}
	build();
	const int m = n;
	for(int i=1;i<=m;i++){
		int op,l,r,c;
		cin>>op>>l>>r>>c;
		if(op==0) insert(l,r);
		else cout<<query(r)<<'\n';
	}
	return 0;
}

标签:入门,分块,int,sqrt,BL,blk,序列,op
From: https://www.cnblogs.com/zheyuanxie/p/blocking-array-thresold.html

相关文章

  • Java8中Optional类入门-替代null避免冗杂的非空校验
    场景Java核心工具库Guava介绍以及Optional和Preconditions使用进行非空和数据校验:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/127683387上面在讲Guava......
  • Seata-php 入门与下半年展望
    通俗地讲,Seata-php是seata的PHP语言实现,实现了Java和PHP之间的互通,让PHPer也能使用seata-php来实现分布式事务。Seata是一个非常成熟的分布式事务框架,在Java......
  • 明解入门练习5-4
    解题关键b[i]=a[4-i],当i=0,b0=a4(草稿记法)#define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>intmain(){inti;inta[5]={17,23,36};intb[5];for(i=0;i......
  • FirewallD 基本知识(FirewallD入门教程)
    centos7中防火墙FirewallD是一个非常的强大的功能了,FirewallD提供了支持网络/防火墙区域(zone)定义网络链接以及接口安全等级的动态防火墙管理工具。它支持IPv4,IPv6......
  • JMeter入门学习
    一、什么是JMeter        ApacheJMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域......
  • 【Golang 快速入门】项目实战:即时通信系统
    即时通信系统-服务端项目架构图: 版本迭代:版本一:构建基础Server版本二:用户上线功能版本三:用户消息广播机制版本四:用户业务层封装版本五:在线用户查询版本六:修改用户名......
  • golang入门项目—日志收集
    传统ELK架构的日志收集:存在的问题:Logstash耗资源较大,运行占用CPU和内存高。另外没有消息队列缓存,存在数据丢失隐患。适用于小规模的集群使用。第二种架构:位于各个节点上......
  • 使用kubeadm搭建多节点k8s集群(chrono《kubernetes入门实战课》笔记整理)
     通过使用minikube,做完了k8s的基础学习和练习,今天开始,使用kubeadm,来搭建更复杂更贴合实际工作的k8s集群。 【集群架构】  多节点集群,应该有大于等于2台node,实验......
  • 极客编程python入门-slots功能
    使用__slots__如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加​​name​​和​​age​​属性。classStudent(object):__slots__=('name','age')#......
  • 后台开发进阶:白话DDD从入门到实践
      尝试用大家都能听得懂的话,结合我们在增值业务中的具体实现,分享一下我们从入门到实践DDD的一些心得。0.写在前面的DDD(领域驱动设计)是EricEvans于2003......