线段树模型及例题整理
线段树的应用范围非常广,可以处理很多与区间有关的题目。

将区间抽象成一个节点,在这个节点中储存这个区间的一些值,那么如果看成节点的话,这就很像一棵满二叉树,所以我们可以用一维数组来储存节点。那么就要考虑父子节点之间的关系。
如果一个一个节点的下标是x,
那么父节点就是x/2
左子节点就是2x(可以写为x<<1),右子节点就是2x+1(可以写为x<<1+1)
那么我们就可以实现节点之间的转移操作,实际上相互影响的也只有父子节点之间相互影响,所以在更新的时候有两种更新方式,一种是用子节点来更新父节点(pushup),一种是用父节点来更新子节点(pushdown)。
还有一个问题就是这个一维数组该开多大,假设区间大小为n,那么我们就将数组开到4n大小。
既然用线段树,那么肯定要定义结构体来表示每个点,结构体的定义首先要有l,r来表示区间范围,然后需要维护的值肯定要放入结构体中,至于其他的变量就要根据维护值是否能用子节点算出父节点来决定,如果不能的话,那么就要考虑引入新的变量。
线段树看起来似乎很高深,但是实际上只有几个函数,模板比较固定:
build()//初始化线段树
modify()//修改
pushup()//用子节点更新父节点
pushdown()//将父节点的更新传到子节点
query() //查询操作
对于这些操作,我们还是结合具体的例题来分析。
1275. 最大数(活动 - AcWing)

题目稍微翻译一下就是,向一个空数组中插入数,在插入的同时进行局部区间最大值的访问。用上线段树后思路也没什么复杂的,我们可以先将整个数组先用线段树维护起来,空位置对应的区间就是0,那么我向末尾插入数的时候,实际上也就相当于对区间进行单点修改,单点修改的话实际上容易想到树状数组,但是树状数组用到了前缀和的原理,前缀和是没办法解决最大值问题的,所以这里还是要用线段树来实现。
我们先来确定结构体如何确定,首先得有l,r来表示区间,然后max也必须定义在结构体中,然后判断是否能用子节点的值来更新父节点,显然是可以的,父区间的最大值=max(左子区间的最大值,右子区间的最大值)。
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
struct node
{int l,r,mx;
}tr[4*N];
int m,p;
void build(int u,int l,int r)
{tr[u]={l,r};if(l==r) return;int mid=l+r>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);}
void pushup(int u)
{tr[u].mx=max(tr[u<<1].mx,tr[u<<1|1].mx);
}
void modify(int u,int x,int c)
{if(tr[u].l==x&&tr[u].r==x) tr[u].mx=c;else{int mid=tr[u].l+tr[u].r>>1;if(x<=mid) modify(u<<1,x,c);else modify(u<<1|1,x,c);pushup(u);}
}
int query(int u,int l,int r)
{if(tr[u].l>=l&&tr[u].r<=r) return tr[u].mx;int mid=tr[u].l+tr[u].r>>1;if(l>mid) return query(u<<1|1,l,r);else if(r<=mid) return query(u<<1,l,r);else return max(query(u<<1,l,r),query(u<<1|1,l,r));
}
int main()
{int last=0,n=0;scanf("%d%d",&m,&p);build(1,1,m);while(m--){char op[2];int a;scanf("%s%d",op,&a);if(op[0]=='A'){a=((long long)last+a)%p;modify(1,++n,a);}else {last=query(1,n-a+1,n);cout<<last<<endl;}}
}
ps:修改单点的话就不用pushdown操作了,pushdown需要用到懒标记,有些麻烦,能不用当然更好。
245. 你能回答这些问题吗(245. 你能回答这些问题吗 - AcWing题库)

这里是单点修改,很容易想到用树状数组来实现,但是树状数组只能维护类似于前缀和这种一整个区间的东西,而我们查询区间中的连续最大子段和,显然用树状数组就没办法实现,那么如果用线段树的话又该怎么实现呢?还是先来看如何定义结构体,显然我们需要储存最大连续子段和,但是这样够不够呢,显然是不够的,因为子节点无法更新父节点,虽然父区间的最大值有可能是左右子区间中的一段,但是还有可能是跨区间的,如果是跨区间的话,要想更新就需要用到左区间的最大后缀和右区间的最大前缀,那么现在就要多维护两个变量——最长前缀和最长后缀,那么现在考虑最长前缀和最长后缀能否能通过子节点来更新父节点,显然是不可以,以最长前缀为例,要用子节点计算的话,有两种情况,一种就是左子节点的最长前缀,一种是左区间加右区间的最大前缀。所以我们实际上还是要维持一个区间和,那么区间和可以由子区间更新父区间吗?当然可以。至此便不用再加别的变量就可以实现这个问题了。
#include<bits/stdc++.h>
using namespace std;
const int N=500010;
int n,m;
int a[N];
struct node{int l,r,mx,lmx,rmx,sum;
}tr[4*N];
void pushup(node &u,node &l,node &r)
{u.mx=max(max(l.mx,r.mx),l.rmx+r.lmx);u.lmx=max(l.lmx,l.sum+r.lmx);u.rmx=max(r.rmx,r.sum+l.rmx);u.sum=l.sum+r.sum;
}
void pushup(int u)
{pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r)
{if(l==r) tr[u]={l,r,a[l],a[l],a[l],a[l]};else{tr[u]={l,r};int mid=l+r>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);pushup(u);}
}
void modify(int u,int x,int v)
{if(tr[u].l==x&&tr[u].r==x) tr[u]={x,x,v,v,v,v};else{int mid=tr[u].l+tr[u].r>>1;if(x<=mid) modify(u<<1,x,v);else modify(u<<1|1,x,v);pushup(u);}
}
node query(int u,int l,int r)
{if(l<=tr[u].l&&tr[u].r<=r) return tr[u];int mid=tr[u].l+tr[u].r>>1;if(l>mid) return query(u<<1|1,l,r);else if(r<=mid) return query(u<<1,l,r);else{auto left=query(u<<1,l,r),right=query(u<<1|1,l,r);node res;pushup(res,left,right);return res;}
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&a[i]);build(1,1,n);while(m--){int op,a,b;scanf("%d%d%d",&op,&a,&b);if(op==1)//最大子段和{if(a>b)swap(a,b);cout<<query(1,a,b).mx<<endl;}else//将a[x]改成y{modify(1,a,b);}}
}
246. 区间最大公约数(246. 区间最大公约数 - AcWing题库)

这里虽然需要修改最大区间,但是修改操作是将区间整体加上一个数,这里可以用差分来处理一下进而避免区间修改操作,毕竟区间修改还怪麻烦的,那么具体该怎么实现呢?维护差分是很容易的,问题在于如何维护gcd的值,这里需要用到欧几里得算法,我们简单证明一下:
gcd(a1,a2,a3,...,an)=gcd(a1,a2-a1,a3-a2,...)
令g=gcd(a1,a2,a3...);
那么a1%g=0,a2%g=0,a3%g=0,...
故而(a2-a1)%g=0,(a3-a2)%g=0,...
故而gcd(a1,a1-a2,a2-a3,...)>=g(gcd(a1,a2-a1,a3-a2,...)是最大公因数,g是一个因数,所以满足大于等于的关系)
令g=gcd(a1,a2-a1,a3-a2,...),
那么a1%g=0,(a2-a1)%g=0,所以a2%g=0,以此类推,a3%g=0,...
故而gcd(a1,a2,a3,...,an)>=g
所以gcd(a1,a2,a3,...,an)=gcd(a1,a2-a1,a3-a2,...)差分数组是什么呢:
b1=a1-a0b2=a2-a1
...
所以我们可以通过维护差分数组来实现。
然后如果需要获得gcd需要维护哪些值呢?gcd(sum(1~bl),gcd(b[l+1],...,b[r]))
区间和这个很容易得到,那么后面的gcd(b[l+1],...,b[r])怎么得到呢,显然如果维和子区间的gcd的话,可以用子区间的gcd得到父区间的gcd,所以就只需要维护区间的gcd和sum即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500010;
int n,m;
long long a[N];
struct node{int l,r;ll sum,g;
}tr[4*N];
ll gcd(ll a,ll b)
{return b?gcd(b,a%b):a;
}
void pushup(node &u,node &l,node &r)
{u.sum=l.sum+r.sum;u.g=gcd(l.g,r.g);
}
void pushup(int u)
{pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r)
{if(l==r) {ll b=a[r]-a[r-1];tr[u]={l,r,b,b};}else{tr[u].l=l,tr[u].r=r;int mid=l+r>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);pushup(u);}
}
void modify(int u,int x,ll v)
{if(tr[u].l==x&&tr[u].r==x) {ll b=tr[u].sum+v;tr[u]={x,x,b,b};}else{int mid=tr[u].l+tr[u].r>>1;if(x<=mid) modify(u<<1,x,v);else modify(u<<1|1,x,v);pushup(u);}
}node query(int u,int l,int r)
{if(l<=tr[u].l&&tr[u].r<=r) return tr[u];int mid=tr[u].l+tr[u].r>>1;if(l>mid) return query(u<<1|1,l,r);else if(r<=mid) return query(u<<1,l,r);//一定要记得写returnelse{auto left=query(u<<1,l,r),right=query(u<<1|1,l,r);node res;pushup(res,left,right);return res;}
}int main()
{scanf("%d%d",&n,&m);a[0]=0;for(int i=1;i<=n;i++) scanf("%lld",&a[i]);build(1,1,n);while(m--){char op[2];int l,r;scanf("%s%d%d",op,&l,&r);if(op[0]=='Q'){auto left = query(1, 1, l);node right({0,0,0,0});if(l+1<=r) right=query(1,l+1,r);cout<<abs( gcd(left.sum,right.g) )<<endl;}else{long long v;scanf("%lld",&v);modify(1,l,v);if(r+1<=n)modify(1,r+1,-v);}}
}
243. 一个简单的整数问题2(活动 - AcWing)

这里是给一段区间同时加上一个数,也很容易想到能不能用差分代替区间修改,但是我们如果用线段树维护差分数组的话,没有办法快速获得区间和,所以这里无可避免的需要实现区间修改,那么就要用到pushdown操作了。
pushdown的操作虽然麻烦,但实际上思路还是比较容易的。 我们在定义区间节点的时候实际上还定义了一个懒标记,对区间进行修改,那么我们我们如果搜到的区间如果被包含在目标区间中,那么我们就将这个区间节点中的懒标记修改一下,然后返回不再往下搜了,如果不完全包含的话,就先将当前搜到区间的懒标记先传下去,然后再执行进一步的递归。这里将懒标记传下去的操作就是pushdown,那么pushdown具体怎么实现呢,实际还要回到懒标记的意义上来,懒标记意味着从当前区间往下所有的区间都需要进行这个修改,当前区间是否执行这个修改无所谓,目的是将修改传到叶子节点,然后由叶子节点往上更新来实现。我们这里定义的话,就直接将修改加到区间上,懒标记表示下面的需要进行的修改。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N];
int n,m;
struct node
{int l,r;long long sum,add;
}tr[4*N];
void pushup(int u)
{tr[u].sum = tr[u<<1].sum+tr[u<<1|1].sum;
}
void pushdown(int u)
{tr[u<<1].add+=tr[u].add,tr[u<<1].sum += (long long)(tr[u<<1].r-tr[u<<1].l+1)*tr[u].add;tr[u<<1|1].add+=tr[u].add,tr[u<<1|1].sum += (long long)(tr[u<<1|1].r-tr[u<<1|1].l+1)*tr[u].add;tr[u].add=0;
}
void build(int u,int l,int r)
{if(l==r) tr[u]={l,r,a[l],0};else{tr[u]={l,r};int mid=l+r>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);pushup(u);}
}
void modify(int u,int l,int r,int v)
{if(l<=tr[u].l&&tr[u].r<=r) {tr[u].sum += (long long)(tr[u].r-tr[u].l+1)*v;tr[u].add+=v;}else{pushdown(u);int mid=tr[u].l+tr[u].r>>1;if(r<=mid) modify(u<<1,l,r,v);else if(l>mid) modify(u<<1|1,l,r,v);else modify(u<<1,l,r,v),modify(u<<1|1,l,r,v);pushup(u);}
}
long long query(int u,int l,int r)
{if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum;pushdown(u);int mid=tr[u].l+tr[u].r>>1;long long v=0;if(l<=mid) v+=query(u<<1,l,r);if(r>mid) v += query(u<<1|1,l,r);return v;
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&a[i]);build(1,1,n);while(m--){char op[2];int l,r;scanf("%s%d%d",op,&l,&r);if(op[0]=='Q'){cout<<query(1,l,r)<<endl;}else{int v;scanf("%d",&v);modify(1,l,r,v);}}
}
247. 亚特兰蒂斯(247. 亚特兰蒂斯 - AcWing题库)


图差不多是这个样子,

单个算还好说,重叠起来就很麻烦。所以这里引入一种全新的算法——扫描线:

我们将图分成如上的区间,显然在一个区间中面积就是区间长度(横坐标差)*区间中纵坐标覆盖的总长度。 假设有一根无限长的直线进行扫描,显然当一个矩形最左边的边被扫到的时候,那么就要开始计算面积了,第二次被扫到的时候就不能再计算这个矩形了。这里我们可以通过对这段区间标记来处理,令左边的标记为1,右边的标记为-1,那么刚好扫到右边的时候就不用再考虑它了。
区间长度倒是还比较好获得,问题在于纵坐标覆盖的总长度该怎么看,显然在一个区间中它是不变的,那么我们可以一个区间一个区间地往后看。那么该如何维护呢?这里有个特别妙地思路,因为y是浮点数,所以需要对y进行离散化,而且我们用线段树维护的时候也并非用区间的端点维护区间,而是在离散化后,得到若干个点,每两点之间的区间我们给它定一个序号,然后我们用线段树的节点来维护每一个区间的序号,这样说还是有点抽象,见下图:

我们看叶子节点,每个叶子节点维护的实际是一个对应序号的区间。
然后再来考虑我们具体需要维护的值是什么,首先我们要直到区间是否被标记才能进一步考虑是否需要把这段长度算上,那么很显然我们需要一个cnt来标记这段区间的标记情况,又因为一个矩形的左右边是对称的,所以cnt的值恒大于等于0,如果cnt>0,那么这段区间就会被考虑,如果cnt==0,那么这段区间就不能再被考虑。
然后需要一个变量len来维护这段区间中被标记部分的总长度。
由于线段树维护的是区间的序号,所以我们每次用到的只有根节点的len,所以在更新的时候,我们是会把cnt传到完全包含的区间部分去的。而且我们每次用的都是根节点的值,所以只要它往上传是正确的,那么就无所谓,所以我们并不需要将父节点的cnt传到子节点去,只要保证子节点的cnt被更新后将len的变化传到父节点去即可。所以一个节点被标记的意义就是这个节点能被表示的所有区间都被标记。
所以在pushup的时候,
如果父节点被标记,那么父节点的len自己用两端点更新一下就好(所以我们更新区间的时候就需要pushup,因为cnt发生了变化);
如果父节点没有被标记,那么就要看它是不是叶子节点,
如果是叶子节点那么len就是0,
否则就需要用它的子节点来更新一下它,因为cnt可能传到它的子节点上去了。
所以我们是先递归然后再进行pushup,这样才能实现从子节点往上在回溯的过程中更新父节点。至此本题相较于模板特殊的地方都讨论完了。
然后这里的离散化就用vector就行,然后查找用二分。
另外需要记录一下左右边。
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
struct edge{double x,y1,y2;int sta;
}seg[2*N];//每个矩形记录左右边
struct node{int l,r,cnt;double len;
}tr[8*N];//4*2*N
bool cmp(edge a,edge b)
{return a.x<b.x;
}
int n;
vector<double>hy;//对y进行离散化
void pushup(int u)
{if(tr[u].cnt) tr[u].len=hy[tr[u].r+1]-hy[tr[u].l];else if(tr[u].l!=tr[u].r){tr[u].len=tr[u<<1].len+tr[u<<1|1].len;}else{tr[u].len=0;}
}
void build(int u,int l,int r)
{tr[u]={l,r,0,0};if(l==r) return;int mid=l+r>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void modify(int u,int l,int r,int k)
{if(l<=tr[u].l&&tr[u].r<=r) {tr[u].cnt+=k;pushup(u);}else{int mid=tr[u].l+tr[u].r>>1;if(l<=mid) modify(u<<1,l,r,k);if(r>mid) modify(u<<1|1,l,r,k);pushup(u);}
}
int find(double x)
{return lower_bound(hy.begin(),hy.end(),x)-hy.begin();
}
int main()
{int t=0;while(~scanf("%d",&n)){hy.clear();t++;if(!n) break;int j=0;for(int i=1;i<=n;i++){double x1,y1,x2,y2;scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);seg[j++]={x1,y1,y2,1},seg[j++]={x2,y1,y2,-1};hy.push_back(y1),hy.push_back(y2);}sort(hy.begin(),hy.end());hy.erase(unique(hy.begin(),hy.end()),hy.end());build(1,0,hy.size()-2);sort(seg,seg+2*n,cmp);double res=0;for(int i=0;i<2*n;i++){if(i) res += (seg[i].x-seg[i-1].x)*tr[1].len;modify(1,find(seg[i].y1),find(seg[i].y2)-1,seg[i].sta);}printf("Test case #%d\n",t);printf("Total explored area: %.2lf\n",res);printf("\n");}
}
1277. 维护序列(活动 - AcWing)


思路:这里和之前不同的地方就在不仅要对一段区间同时加上一个数,还要对一段区间同时乘上一个数,所以就涉及到先后顺序的问题了,所谓先后怎么说呢,实际上是父节点去更新子节点的时候对子节点产生的先后。因为子节点本身是有更新的,那么它被父节点更新的时候是先加还是先乘呢:
我们来分别讨论,如果是先加再乘:
(x+a)*b
那么父节点的懒标记传过来的时候,哪怕只有加c
(x+a)*b+c
是没有办法变成(x+_)*_的形式的,所以先加再乘不合适
那么如果是先乘再加呢:
x*b+a
父节点的懒标记传过来的时候:
x*b+a+c
(x*b+a)*mul=x*b*mul+a*mul
都是可以变成x*_+_的形式的,所以我们就定义先乘再加。
那么更新的时候既有乘的操作又有加的操作,实际上还是有点麻烦的,所以我们这么来,如果是加x的话就定义成*1和+x), 如果是*x的话就定义成*x和+0.这样写一个modify函数就够了。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
typedef long long ll;
int a[N];
int n,m,p;
struct node{int l,r;int sum,add,mul;
}tr[4*N];
void pushup(int u)
{tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p;
}
//(x*a+b)*c+d=x*a*c+b*c+d
void eval(node &u,int mul,int add)
{u.sum=((ll)u.sum*mul+(ll)(u.r-u.l+1)*add)%p;u.add=((ll)u.add*mul+add)%p;u.mul=(ll)u.mul*mul%p;
}
void pushdown(int u)
{eval(tr[u<<1],tr[u].mul,tr[u].add);eval(tr[u<<1|1],tr[u].mul,tr[u].add);tr[u].add=0,tr[u].mul=1;
}
void build(int u,int l,int r)
{if(l==r) tr[u]={l,r,a[l],0,1};else{tr[u]={l,r,0,0,1};//反正子节点还要来更新它的int mid=l+r>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);pushup(u);}
}
void modify(int u,int l,int r,int mul,int add)
{if(l<=tr[u].l&&tr[u].r<=r) {eval(tr[u],mul,add);}else{pushdown(u);int mid=tr[u].l+tr[u].r>>1;if(l<=mid) modify(u<<1,l,r,mul,add);if(r>mid) modify(u<<1|1,l,r,mul,add);pushup(u);}
}
int query(int u,int l,int r)
{if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum;pushdown(u);int mid=tr[u].l+tr[u].r>>1;int v=0;if(l<=mid) v=query(u<<1,l,r);if(r>mid) v=(v+query(u<<1|1,l,r))%p;return v;
}
int main()
{scanf("%d%d",&n,&p);for(int i=1;i<=n;i++) scanf("%d",&a[i]);build(1,1,n);scanf("%d",&m);while(m--){int op,l,r,v;scanf("%d%d%d",&op,&l,&r);if(op==1)//*{scanf("%d",&v);modify(1,l,r,v,0);}else if(op==2)//+{scanf("%d",&v);modify(1,l,r,1,v);}else//q{cout<<query(1,l,r)<<endl;}}
}相关文章:
线段树模型及例题整理
线段树的应用范围非常广,可以处理很多与区间有关的题目。 将区间抽象成一个节点,在这个节点中储存这个区间的一些值,那么如果看成节点的话,这就很像一棵满二叉树,所以我们可以用一维数组来储存节点。那么就要考虑父子节…...
揭秘Java性能调优的层次 | 综合多方向提升应用程序性能与系统高可用的关键(架构层次规划)
揭秘性能调优的层次 | 综合多方向提升应用程序性能与系统的高可用 前言介绍调优层次调优 — 设计案例说明 - 操作轮询控制事件驱动 调优 — 代码案例说明 - ArrayList和LinkedList性能对比案例说明 - 文件读写实现方式的性能对比 调优 — JVMJVM架构分布JVM调优方向**JVM垃圾回…...
事件循环解析
浏览器的进程模型 何为进程? 程序运行需要有它自己专属的内存空间,可以把这块内存空间简单的理解为进程 每个应用至少有一个进程,进程之间相互独立,即使要通信,也需要双方同意。 何为线程? 有了进程后&…...
物联网技术助力智慧城市安全建设:构建全方位、智能化的安全防护体系
一、引言 随着城市化进程的加速和信息技术的迅猛发展,智慧城市已成为现代城市发展的重要方向。在智慧城市建设中,安全是不可或缺的一环。物联网技术的快速发展为智慧城市安全建设提供了有力支持,通过构建全方位、智能化的安全防护体系&#…...
mac打不开xxx软件, 因为apple 无法检查其是否包含恶意
1. 安全性与隐私下面的允许来源列表,有些版本中的‘任何来源’选项被隐藏了,有些从浏览器下载的软件需要勾选这个选项才能安装 打开‘任何来源’选项 sudo spctl --master-disable 关闭‘任何来源’选项 sudo spctl --master-enable...
《深入浅出红黑树:一起动手实现自平衡的二叉搜索树》
一、分析 1. 红黑树的性质 红黑树是一种自平衡的二叉搜索树,它具有以下五个性质: (1)节点是红色或黑色。 (2)根节点是黑色。 (3)所有叶子节点(NIL节点)是…...
C++——模版
前言:哈喽小伙伴们好久不见,这是2024年的第一篇博文,我们将继续C的学习,今天这篇文章,我们来习一下——模版。 目录 一.什么是模版 二.模版分类 1.函数模版 2.类模板 总结 一.什么是模版 说起模版,我们…...
《TCP/IP详解 卷一》第9章 广播和组播
目录 9.1 引言 9.2 广播 9.2.1 使用广播地址 9.2.2 发送广播数据报 9.3 组播 9.3.1 将组播IP地址转换为组播MAC地址 9.3.2 例子 9.3.3 发送组播数据报 9.3.4 接收组播数据报 9.3.5 主机地址过滤 9.4 IGMP协议和MLD协议 9.4.1 组成员的IGMP和MLD处理 9.4.2 组播路由…...
备战蓝桥杯---动态规划的一些思想1
话不多说,直接看题: 目录 1.双线程DP 2.正难则反多组DP 3.换个方向思考: 1.双线程DP 可能有人会说直接贪心:先选第1条的最优路径,再选第2条最优路径。 其实我们再选第1条时,我们怎么选会对第2条的路径…...
基于BERTopic模型的中文文本主题聚类及可视化
文章目录 BERTopic简介模型加载地址文本加载数据处理BERTopic模型构建模型结果展示主题可视化总结BERTopic简介 BERTopic论文地址:BERTopic: Neural topic modeling with a class-based TF-IDF procedure BERTopic是一种结合了预训练模型BERT和主题建模的强大工具。它允许我…...
MySQL:函数
提醒: 设定下面的语句是在数据库名为 db_book里执行的。 创建user_info表 注意:pwd为密码字段,这里使用了VARCHAR(128)类型,为了后面方便对比,开发项目里一般使用char(32),SQL语句里使用MD5加密函数 USE db…...
C/C++内存管理及内存泄漏详解
目录 C/C内存分布 C语言中动态内存管理方式:malloc/calloc/realloc/free C内存管理方式 new/delete操作内置类型 new和delete操作自定义类型 operator new与operator delete函数 new和delete的实现原理 内置类型 自定义类型 内存泄漏 概念 内存泄漏分类 ⭐…...
什么是系统工程(字幕)41
0 00:00:01,650 --> 00:00:01,884 好 1 00:00:01,884 --> 00:00:06,330 那这个时候我们就可以把它绑定到上面了 2 00:00:06,610 --> 00:00:07,940 那我们来看 3 00:00:11,710 --> 00:00:12,930 幻灯片上 4 00:00:15,530 --> 00:00:15,885 5 00:00:15,885 --…...
测开新手:pytest+requests+allure自动化测试接入Jenkins学习
最近在这整理知识,发现在pytest的知识文档缺少系统性,这里整理一下,方便后续回忆。 在python中,大家比较熟悉的两个框架是unittest和pytest: Unittest是Python标准库中自带的单元测试框架,Unittest有时候…...
学习网络编程No.11【传输层协议之UDP】
引言: 北京时间:2023/11/20/9:17,昨天成功更文,上周实现了更文两篇,所以这周再接再厉。当然做题任在继续,而目前做题给我的感觉以套路和技巧偏多,还是那句话很多东西不经历你就是不懂ÿ…...
向爬虫而生---Redis 基石篇6 <拓展HyperLogLog>
前言: 继续之前的 向爬虫而生---Redis 基石篇5 <拓展Zset>-CSDN博客 一些比较基础的redis类型在初中级阶段用着没有毛病,但是到了大数据时代,慢慢一些更高级的场景,就需要把这几个类型搬出来了! 正文: 概念: 当我们需要对一个大型数据集进行去重计…...
JavaScript中的this
在实际应用中,了解 this 的行为是非常重要的,特别是在编写库或框架时,或者当你需要在回调函数中访问特定的上下文时,通常推荐使用箭头函数或者其他方法来确保 this 的正确指向。 在ES6中,this 的值取决于它是如何被调用…...
宝塔php站点设置伪静态规则 访问 a.com 时候跳转到 a.com/b.html
要在宝塔 PHP 站点中设置伪静态规则,实现访问a.com时跳转到a.com/b.html,可以按照以下步骤进行操作: 打开宝塔面板并登录到你的服务器管理界面。进入网站设置页面,找到你要设置伪静态规则的 PHP 站点。在站点设置中,找…...
git介绍4.2
git(版本控制工具) 一、git 介绍 1、git是目前世界上最先进的分布式版本控制系统,可以有效,高速的处理从小到大的项目版本管理。 2、git是linux torvalds 为了帮助管理linux内核开发二开发的一个开放源码的版本控制软件。 3、git作用:更好…...
【深入了解设计模式】组合设计模式
组合设计模式 组合模式是一种结构型设计模式,它允许你将对象组合成树状结构来表现“整体-部分”关系。组合模式使得客户端可以统一对待单个对象和组合对象,从而使得代码更加灵活和易于扩展。 概述 对于这个图片肯定会非常熟悉,上图我们可…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
