树分治全家桶

news/2024/11/19 10:25:25/文章来源:https://www.cnblogs.com/binbin200811/p/18554349

树分治全家桶

树,(是一种益于保护环境植物)是图论当中的一种特殊图,由于(绿化环境的作用非常优秀)特殊性质丰富,经常出现在我们身边。

本文将主要介绍(如何植树)一种树上优美的暴力——树分治。

树分治

树分治可以将部分暴力降至 \(O(\log n)\)\(O(\log^2 n)\) 级别,适用于树上路径的相关查询及其变形,树上某一类点的查询等。

Part1 点分治

点分治作为树分治的基础思想,主要利用树的重心进行不断划分。

例1:P3806 【模板】点分治 1

暴力枚举起点处理每一个询问,复杂度 \(O(n^2)\)

从树的重心入手,找出重心(如果有多个任选其一)。

上图的树中,\(2\) 节点为树的重心,我们称其为第一层分治中心。

\(2\) 为根,求出到各个点的距离,得出以 \(2\)一个端点的所有路径的长度。

现在考虑一条穿过 \(2\) 的路径,这样的路径可以由两条不在 \(2\) 的同一子树内的路径构成(如路径 \(8\to 10\) 可以由 路径 \(2\to8\) 与路径 \(2\to 10\) 相加得到),将这两条路径相加可以得到一条穿过 \(2\) 的路径。

处理询问时,我们枚举一棵子树内所有路径长度 \(dis\),查询之前是否标记了长为 \(dis-k\) 的另外子树一棵中的路径。在枚举下一棵子树前,标记该子树中所有的路径长度。

现在,所有穿过 \(2\) 的(包括以 2 为端点的)路径都被考虑了。那么后面求的路径都和 2 无关,直接把 2 删除。

图变为:

现在形成了森林,对于每一棵新树,我们分别求出树的重心,他们是 \(4,6,5,10\),称其为第二层分治中心。对于每个分治中心,重复对 \(2\) 所进行的查找路径到处理询问的操作,考虑每一棵树内穿过分治中心的路径。

删除第二层分治中心,对于新森林重复上述操作。依次做下去直到删除完最后的节点,此时原树的路径都被且仅被一个分治中心考虑到,正确性显然。

分析时间复杂度,每层的分治中心所遍历的节点的总和是 \(O(n)\) 级别的,根据树的重心的定义,每个分治中心删除后其剩余的最大子树大小不大于其的 \(\frac{1}{2}\),显然至多只有 \(\log n\) 层节点,求固定端点的路径长度的时间复杂度达到了优秀的 \(O(n\log n)\),由于后面还有枚举子树的路径长度与查询的路径长度,所以瓶颈是 \(O(nm\log n)\)

例题代码:

#include<bits/stdc++.h>
using namespace std;const int maxn=2e4+5;struct Edge
{int tot;int head[maxn];struct edgenode{int to,nxt,w;}edge[maxn*2];inline void add(int x,int y,int z){tot++;edge[tot].to=y;edge[tot].w=z;edge[tot].nxt=head[x];head[x]=tot;}
}T;int n,m,tot,rt,crem;
int dis[maxn],q[maxn],rem[maxn],mx[maxn],qry[maxn];bool jug[maxn*maxn],ans[maxn];int siz[maxn];
bool book[maxn],cut[maxn];
inline void dfs_siz(int u)//求出以 u 为根的子树大小
{book[u]=true;siz[u]=1;for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(book[v]||cut[v]) continue;dfs_siz(v);siz[u]+=siz[v];}book[u]=false;
}
inline int dfs_rt(int u,const int tot)//返回重心,tot 为当前树的节点总个数
{book[u]=true;int ret=u;for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(book[v]||cut[v]) continue;if(siz[v]*2>=tot){ret=dfs_rt(v,tot);break;}}book[u]=false;return ret;
}
inline void dfs_dis(int u)//求出重心到 u 的距离,并记录在数组 rem 中
{book[u]=true;rem[++crem]=dis[u];for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(cut[v]||book[v]) continue;dis[v]=dis[u]+T.edge[i].w;dfs_dis(v);}book[u]=false;
}
inline void calc(int u)//考虑穿过重心的路径
{queue<int>que;while(!que.empty()) que.pop();for(int i=T.head[u];i;i=T.edge[i].nxt)//枚举子树{int v=T.edge[i].to;if(cut[v]) continue;crem=0,dis[v]=T.edge[i].w;dfs_dis(v);for(int j=1;j<=crem;j++)//枚举路径长度for(int k=1;k<=m;k++)//枚举询问{if(qry[k]>=rem[j]) ans[k]|=jug[qry[k]-rem[j]];//查询其它子树是否存在长为 qry[k]-rem[j] 的路径}for(int j=1;j<=crem;j++) que.push(rem[j]),jug[rem[j]]=1;//标记存在一棵子树有长为 rem[j] 的路径}while(!que.empty()) jug[que.front()]=0,que.pop();
}
inline void dfs(int u)//处理 u 所在的新树
{dfs_siz(u);int g=dfs_rt(u,siz[u]);cut[g]=1;//将重心 g 删除(标记为 1)jug[0]=1;//g->g 的路径,方便与其他路径相加形成 g->x 的路径calc(g);for(int i=T.head[g];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(cut[v]) continue;dfs(v);}
}int main()
{scanf("%d%d",&n,&m);for(int i=1;i<n;i++){int x,y,z;scanf("%d%d%d",&x,&y,&z);T.add(x,y,z);T.add(y,x,z);}for(int i=1;i<=m;i++) scanf("%d",&qry[i]);dfs(1);for(int i=1;i<=m;i++){if(ans[i]) printf("AYE\n");else printf("NAY\n");}
}

总结

点分治本质是将图进行分治后固定分治中心跑单点的暴力,然后合并不同路径的答案,通常运用枚举子树的方式去重。

当然,这种算法并不支持在线处理。

Part2 边分治

边分治与点分治类似,不过是冷门算法。

选择一条边,这条边分成两边的树的大小最均匀,分别暴力处理两边树的信息,在通过边把所得的信息链接起来。

但如果图是菊花图的话边分治会被卡成 \(O(n^2)\)

咋办哩?

把原树转成一棵二叉树,像这样:

Ps:图片引自 OI-wiki 树分治章节。

转成二叉树后消除了菊花图的弊端,使得边分治具有普遍性。

由于至多增加 \(n\) 个点,总复杂度 \(O(n\log n)\)

点分治的题大部分边分治也可以做。

Part3 点分树(动态树分治)

树分治的终极版本,改变原树形态使得层数变为 \(\log n\) 的一种重构树,通常解决与原树形态无关的带修改或在线的问题。

点分治时,我们会删除该分治中心,形成了若干棵子树,将该分治中心与这些子树的分治中心连边,形成了一棵重构树。

根据点分治的时间复杂度证明,这棵树的深度小于 \(\log n\)

下面是原树改为重构树后的示例。

原树:

树分树后的重构树:

它牺牲了原来的结构来换取高速的结构,所以如果子树和父节点有路径(改变距离、一次性更改子树内所有节点、断开边、重连边……)关系,它就完了。

所以点分树和原树的常见性质是:

  1. 点分树内的一棵子树是原树的一个联通块。
  2. 点分树上两点的 \(Lca\) 在原树两点的路径上。
  3. 一个分治中心除了原树向上的那棵子树,其他子树两两的 \(Lca\) 为自己本身。

在点分树上有很多反直觉的地方:

  1. 点分树上的点对 \((u,v)\) 间在原树上的距离与其在点分树上的距离无关(特别注意与祖先的原树距离也与点分树上的距离无关)。
  2. 点分树上的祖先、兄弟关系均与原树无关。

它最重要的是支持在线更改部分信息!

那这种树可以干什么呢?

例2:P6329 【模板】点分树 | 震波

暴力而言,遍历周围距离不超过 \(k\) 的点,时间复杂度 \(O(qn)\)

下文无特殊说明树均为点分树,每一个树上的节点 \(u\),设 \(w[u][i]\) 为原树上距离 \(u\)\(i\) 的点分树上属于自己子树的点的权值和。

形式化的,\(w[u][i]=\sum_{v\in u.sontree} [dis(u,v)==i]\times val[v]\),其中子树为点分树的 \(u\) 子树,距离 \(dis(u,v)\)\(u,v\) 原树上的距离(下同)。

对于 \(u\) 的询问从点分树上的节点 \(u\) 开始,先查询 \(\sum_{i=0}^k w[u][i]\)。此时,点分树上 \(u\) 的子树就已经查询完毕,也就是对应,原树上的一个连通块内的点都被计算了与 \(u\) 贡献。

\(u\) 向上跳到父亲(此处为点分树上的父亲,下同),查询 \(\sum_{i=0}^{k-dis(u,fa_u)} w[fa_u][i]\),这样可以查询出 \(fa_u\) 子树的形成的连通块内的点与 \(u\) 的贡献。但此时发现一个问题,如果 \(u\) 子树内的一个点距 \(x\),满足 \(dis(u,x)\leq k\)\(dis(x,fa_u)\leq k-dis(u,fa_u)\),那么这个点会被计算两次贡献。

一个简单的想法是,每次在 \(fa_u\) 处查询时,减去一次 \(u\) 子树内满足以上两种条件的点的权值。

那么我们记录 \(w_0[u][i]\)\(dis(fa_u,v)=i\)\(v\) 属于 \(u\) 的子树。

形式化的,\(w_0[u][i]=\sum_{v\in u.sontree} [dis(fa_u,v)==i]\times val[v]\)

\(fa_u\) 处查询时,减去 \(\sum_{i=0}^{k-dis(u,fa_u)} w_0[u][i]\) 即完成了对 \(fa_u\) 的去重。

现在将查询范围 \(u\) 子树形成的连通块扩大到了 \(fa_u\) 形成的连通块,不断的向上跳父亲,重复查找与去重操作,最终在至多 \(\log n\) 次操作后,我们将查询范围扩展到了整一棵树。

修改一个点 \(u\) 时,不难发现包含 \(u\) 权值的点只在 \(u\) 的点分树的祖先处,不断向上爬点分树更改 \(w_0\)\(w\) 就可以完成一次修改。

使用动态开点线段树或 \(vector\) 结合连通块的实际大小实现的树状数组,可以将单次查询前缀和与修改降至 \(O(\log n)\) 级,由于至多向上跳 \(\log n\) 次祖先,每一次查询修改 \(O(\log^2 n)\) 的复杂度,总复杂度 \(O(n\log^2 n+q\log^2 n)\)

#include<bits/stdc++.h>
using namespace std;#define inf 1e8const int maxn=1e5+5;struct Edge
{int tot;int head[maxn];struct edgenode{int to,nxt;}edge[maxn*2];inline void add(int x,int y){tot++;edge[tot].to=y;edge[tot].nxt=head[x];head[x]=tot;}
}T;//原树边
struct Tree//线段树
{int ct;int rt[maxn];struct node{int ch[2],val;}tree[maxn*55];void insert(int &p,int l,int r,int x,int y){if(!p) p=++ct;if(l==r){tree[p].val+=y;return ;}int mid=(l+r)>>1;if(x<=mid) insert(tree[p].ch[0],l,mid,x,y);else insert(tree[p].ch[1],mid+1,r,x,y);tree[p].val=tree[tree[p].ch[0]].val+tree[tree[p].ch[1]].val;}int query(int p,int l,int r,int ql,int qr){if(ql>qr) return 0;if(!p) return 0;if(ql<=l&&r<=qr) return tree[p].val;if(l>qr||r<ql) return 0;int mid=(l+r)>>1;return query(tree[p].ch[0],l,mid,ql,qr)+query(tree[p].ch[1],mid+1,r,ql,qr);}
}w[2];//w[0] 同上文 w_0,w[1] 同上文 wint n,tot,sum,m;
int val[maxn],siz[maxn],dis[maxn][25],fa[maxn],K[maxn];
//此处的 K 记录了 u 处在第几层,dis[u][i] 为 u 与第 i 层的祖先的距离
//使用 O(1) lca 或 vector<pair> 记录会获得更优美的实现
vector<int>E[maxn];
//记录点分树上的边
bool cut[maxn],book[maxn];inline void dfs_siz(int u)
{book[u]=true;siz[u]=1;for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(book[v]||cut[v]) continue;dfs_siz(v);siz[u]+=siz[v];}book[u]=false;
}
inline int dfs_rt(int u,const int tot)
{book[u]=true;int ret=u;for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(book[v]||cut[v]) continue;if(siz[v]*2>=tot){ret=dfs_rt(v,tot);break;}}book[u]=false;return ret;
}
inline void dfs_dis(int u,int k)//求点分树上的第 k 层父亲与 u 的距离
{book[u]=true;for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(cut[v]||book[v]) continue;dis[v][k]=dis[u][k]+1;dfs_dis(v,k);}book[u]=false;
}
inline void dfs_calc(int u,int op,int st,int k)//将贡献加入 w[0] 与 w[1]
{book[u]=true;w[op].insert(w[op].rt[st],0,inf,dis[u][k],val[u]);for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(cut[v]||book[v]) continue;dfs_calc(v,op,st,k);}book[u]=false;
}inline void dfs(int u,int f)
{dfs_siz(u);int g=dfs_rt(u,siz[u]);cut[g]=true;if(f) dfs_calc(g,0,g,K[f]);E[f].push_back(g);fa[g]=f;K[g]=K[fa[g]]+1;dfs_dis(g,K[g]);dfs_calc(g,1,g,K[g]);for(int i=T.head[g];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(cut[v]) continue;dfs(v,g);}
}void change(int u,int st,int now)//修改
{if(!u) return ;w[1].insert(w[1].rt[u],0,inf,dis[st][K[u]],now-val[st]);//修改 wif(fa[u]) w[0].insert(w[0].rt[u],0,inf,dis[st][K[u]-1],now-val[st]);//修改 w[0]change(fa[u],st,now);//向上跳
}
int dfs_ans(int u,int v,int st,int d)
{if(!u) return 0;if(d-dis[st][K[u]]<0) return dfs_ans(fa[u],u,st,d);//此处距离超过查询范围,向上跳int res=w[1].query(w[1].rt[u],0,inf,0,d-dis[st][K[u]]);//查询if(v) res-=w[0].query(w[0].rt[v],0,inf,0,d-dis[st][K[u]]);//去重return res+dfs_ans(fa[u],u,st,d);//跳父亲
}int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&val[i]);for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);T.add(u,v);T.add(v,u);}dfs(1,0);int ans=0;for(int i=1;i<=m;i++){int op,x,y;scanf("%d%d%d",&op,&x,&y);x^=ans,y^=ans;if(op){change(x,x,y);val[x]=y;}else{printf("%d\n",ans=dfs_ans(x,0,x,y));}}
}

小结

点分树处理的问题多是带修的或强制在线的,通常与带 \(\log\) 的数据结构结合,虽然复杂度有保证,但无可避免的是常数偏大。

若本题不带修,将询问离线至点,使用朴素的前缀和(每次连通块的大小缩小 \(\frac{2}{1}\),长度也缩小 \(\frac{1}{2}\),前缀和复杂度也与 \(O(n\log n)\) 同级),可以做到 \(O(n\log n+q\log n)\)

例3:ZJOI2007 捉迷藏

建处点分树,每个节点用 set 存与子树内黑点的距离,并将两个黑点的距离相加求出穿过该节点的最长黑点距离,全局用一个 set 维护这个信息。

对于一个修改,除了改变每个节点的 set 还改变将改变全局的 set,从 \(x\) 向上跳点分树,类似于初始化时的修改即可。

时间复杂度 \(O(n\log^2 n)\)

例4:P3345 ZJOI2015 幻想乡战略游戏

这道题是一道点分树的好题,巧妙的运用了点分树与原树的性质,利于更深刻的理解树分治。

同时使用了一个重要的技巧:点分树上二分查找关键点。

证明补给点是树的带权重心,即该删除该节点后,分出来的若干连通块没有一个点的权值和超过总权值和的一半。

带有贪心的证明:如果点 \(u\) 不是带权重心,那么与点 \(u\) 相连的连通块一定有一个权值和 \(wsiz\geq \frac{wtot}{2}\),其中 \(wsiz\) 为连通块的权值和,\(wtot\) 为权值总和。

如果钦定补给站为点 \(u\),不如把补给站向权值和大于 \(\frac{wtot}{2}\) 的联通块挪动一个点,所带来的变化量是:\(val\times (wtot-wsiz)-val\times wsiz=val\times wtot-2\times val\times wsiz\),而根据定义,我们有 \(wsiz\times 2\geq wtot\),所以 \(val\times wtot-2\times val\times wsiz\leq 0\),当且经当 \(wsiz=\frac{wtot}{2}\) 时等号成立。

树上的带权重心可能有多个,但这些带权重心的答案是一样的,可以参考等号成立的情况来寻找多个带权重心(他们一定时相邻的)。

在原树上,我们可以先钦定一个点作为补给站,通过上述贪心的转移补给站所位于的点即可。

这样的时间复杂度是一次查询 \(O(n)\)

我们需要更高效的算法,其实由于和原树形态有关,很难想到树分治,但总有些人脑回路不正常。

——通过点分树的形式对树进行二分查找。

但由于点分树和原树形态的区别,所以点分树上的一棵子树不可以代表原树中的一棵子树,所以就不可以通过贪心的策略用边转移了。

解决方法是,把一个子树看做一个连通块,将这个连通块的信息(权值和与点数和)集中在子树的根,通过查找的方式,不断跳补给站要求的连通块的子树根,即满足 \(wsiz\geq \frac{wtot}{2}\) 的连通块的子树根。

易证明在原树上满足这样要求的点,在点分树上祖先一定都满足这个关系。

当我们从 \(f\) 跳到一个子树根 \(u\) 后,进一步寻找时,那些可能在点分树中是 \(u\) 的子孙而在原树中是 \(u\) 祖先的节点,他们所表示的权值和应该是在原树上以 \(u\) 为根的信息,可此时表示的是以 \(f\) 为根的信息,所以需要增加上 \(f\) 连通块的权值减去 \(u\) 连通块的权值来更新这些点。不难发现这样的点全不属于一棵 \(u\) 的儿子子树,我们只需要更新这棵儿子子树的根,保证下一步正确。

简单的,把连通块 \(u\)\(f\) 相连的点 \(i\) 设为 \(u\) 的子树的接入点,每次暴力的更改 \(i\) 在点分树上的权值(显然也要更改点分树上祖先的权值),这样可以保证对于 \(u\) 来说,下一步是正确的。跳向下一个节点后,重复同样的操作,即可以保证每一次的下一步都是正确的。

上面的算法求出补给站的位置,后面的问题是简单的。

每个点分树上的点存下子树内的权值和与路径乘权值的和,对于此时做单点查询向上收集这些值并去重即可。

#include<bits/stdc++.h>
using namespace std;#define ll long longconst int maxn=1e5+5;struct Edge
{int tot;int head[maxn];struct edgenode{int to,nxt,val;}edge[maxn*2];inline void add(int x,int y,int z){tot++;edge[tot].to=y;edge[tot].nxt=head[x];edge[tot].val=z;head[x]=tot;}
}T;struct ed{int v;ll dis;};
vector<ed>fa[maxn],s[maxn];int n,m,rt;
ll wtot;
int siz[maxn],itr[maxn];
bool cut[maxn],book[maxn];
ll dep[maxn],rel[maxn],wsiz[maxn];inline int dfs1(int u)//处理原树 siz
{book[u]=true;for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(!book[v]&&!cut[v]) siz[u]+=dfs1(v);}book[u]=false;return siz[u];
}
inline int fr(int u,const int &tot)//找重心
{book[u]=true;int ret=u;for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(!book[v]&&!cut[v]&&2*siz[v]>=tot){ret=fr(v,tot);break;}}book[u]=false;return ret;
}
inline void dfs2(int u,const int &g)//预处理点分树的距离
{book[u]=true;fa[u].push_back({g,dep[u]});for(int i=T.head[u];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(book[v]||cut[v]) continue;dep[v]=dep[u]+T.edge[i].val;dfs2(v,g);}book[u]=false;siz[u]=1;return ;
}
inline void solve(int u,const int &f)//建点分树
{dfs1(u);int g=fr(u,siz[u]);cut[g]=true;itr[g]=u;s[f].push_back({g,0}),fa[g].push_back({g,0});for(int i=T.head[g];i;i=T.edge[i].nxt){int v=T.edge[i].to;if(!cut[v]){dep[v]=T.edge[i].val;dfs2(v,g);solve(v,g);}}rt=g;
}
inline void modify(int u,int e)//每次修改操作,修改树上点权
{wtot+=e;int p=u;rel[u]+=e;for(auto i:fa[u]) wsiz[i.v]+=e;for(int i=fa[u].size()-1;i>=0;p=fa[u][i].v,i--){for(auto &j:s[fa[u][i].v])if(j.v==p){j.dis+=e*fa[u][i].dis;break;}}
}
inline void modi(int u,int e){for(auto i:fa[u]) wsiz[i.v]+=e;}//暴力修改点权
inline int find(int u)//树上二分
{int ret=u;for(auto i:s[u]){if(wsiz[i.v]*2>=wtot){int del=wsiz[u]-wsiz[i.v];modi(itr[i.v],del);ret=find(i.v);modi(itr[i.v],-del);break;}}return ret;
}
inline ll qry(int u)//查询
{ll ret=0;int p=u;for(int i=fa[u].size()-1;i>=0;p=fa[u][i].v,i--){ret+=fa[u][i].dis*rel[fa[u][i].v];int f=fa[u][i].v;for(auto j:s[f]) if(j.v!=p){ret+=fa[u][i].dis*wsiz[j.v]+j.dis;}}return ret;
}int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) siz[i]=1;for(int i=1;i<n;i++){int x,y,z;scanf("%d%d%d",&x,&y,&z);T.add(x,y,z),T.add(y,x,z);}solve(1,0);for(int i=1,u,e;i<=m;i++){scanf("%d%d",&u,&e);modify(u,e);printf("%lld\n",qry(find(rt)));}
}

Part 4 点分树中的去重问题

  1. 最值查询:单一的最值查询即使出现重复绝大多数情况也不影响。而针对最大值个数一类问题,可以利用 stl 或者线段树维护来自不同分治中心的最值及其个数。
  2. 路径和查询:一般在点分树节点的儿子处减去在父亲查询时求和的多余部分。

而使用点分治枚举子节点,在某些情况下可以跳过去从这一步骤。

练习

P6626 省选联考 2020 B 卷 消息传递

P4178 Tree

P7215 JOISC2020 首都

P5311 Ynoi2011 成都七中

P2664 树上游戏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/836678.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

模拟计算hash前面N个0需要的时间

写了一个python代码用来模拟计算当hash前面有N个0时需要多长时间。 代码如下: import hashlib import time from datetime import timedelta from plyer import notificationdef find_hash_with_prefix_zeros(prefix_length=6):# 初始字符串base_text = "Hello, World!&q…

StopWatch使用

调试查看耗时的一个便捷类,一般只使用三个方法StopWatch stopWatch = new StopWatch();stopWatch.start("获取对象");//逻辑代码xxxxxxxxxxxstopWatch.stop();log.info(stopWatch.prettyPrint()); //打印详细信息

IDEA svn项目 更换SVN地址

原svn项目地址URL1,后来把项目移到了URL2,但自己的SVN地址是URL1且已改了好多东西,不想down URL2源码再改。 解决方法: 原文链接:https://blog.csdn.net/weixin_35721320/article/details/79290369

值得推荐的IT公司名单(广州篇)

广州,作为中国南方的重要城市,不仅历史悠久,而且在科技创新方面也具有极高的活力。特别是在IT领域,广州孕育了许多知名企业和创业公司,成为众多技术人才的聚集地。本文将介绍一些在广州值得去的IT公司,为那些希望在广州发展的技术人才提供参考。 互联网大厂 1、腾讯:作为…

项目经理必备:如何通过管理工具提高执行效率?

在瞬息万变的商业环境中,项目经理如同掌舵人,引领团队穿越风浪,确保项目按时、按质、按预算完成。然而,面对日益复杂的项目需求和不断变化的市场环境,仅凭传统的管理方式已难以满足高效执行的需求。此时,借助先进的项目管理工具,成为了项目经理提升执行效率、实现项目成…

PAM限制实测

计算节点pam限制调度系统slurm提供了pam插件,可以实现如下功能: 当且仅当计算节点运行普通用户作业的情况下,该用户才可以通过ssh登录该计算节点。 计算节点的调度系统安装后,在/lib64/security下存在如下三个库文件: /lib64/security/pam_slurm_adopt.a /lib64/security/…

linux学习day02_常用命令学习

1、那个 ~ 符号代表的是“使用者的主文件夹”的意思,他是个“变量!”举例来说,root的主文件夹在/root, 所以 ~ 就代表/root的意思 至于提示字符方面,在Linux当中,默认root的提示字符为 # ,而一般身份使用者的提示字符 为 $ .在 Linux 系统中,英文大小写字母是不一样的。…

赋能业务,驱动未来,科华数据召开CRM平台升级项目启动会

11月13日,全球卓越的智慧电能解决方案提供商-科华数据股份有限公司(以下简称“科华数据”)CRM平台升级项目启动会在厦门顺利举行。科华集团总裁陈四雄、科华数能总裁崔剑、科华数通副总裁林清民、纷享销客高级副总裁江水、中南战区交付总经理徐延涛、深圳分公司总经理杨小会…

使用Pytorch构建视觉语言模型(VLM)

视觉语言模型(Vision Language Model,VLM)正在改变计算机对视觉和文本信息的理解与交互方式。本文将介绍 VLM 的核心组件和实现细节,可以让你全面掌握这项前沿技术。我们的目标是理解并实现能够通过指令微调来执行有用任务的视觉语言模型。 总体架构VLM 的总体架构包括:图…

实现高性能数据同步:旺店通数据写入金蝶云星空

高效数据集成案例:从旺店通到金蝶云星空旺店通其他出库单同步--114:从旺店通企业奇门到金蝶云星空的数据集成案例 在现代企业的运营中,数据的高效流动和精准管理是提升业务效率的关键。本文将分享一个实际运行的系统对接集成案例——“旺店通其他出库单同步--114”,展示如何…

高效团队如何破除跨部门协作的瓶颈?这篇文章告诉你!

在瞬息万变的商业环境中,企业面临着愈加复杂的挑战:跨部门的协作如何更高效?资源分配如何更精准?项目进度如何实时可控?这一系列问题的解决方案,不再仅仅依赖传统的经验管理,而是逐步转向技术驱动。项目管理软件正是在这样的需求场景中诞生,并快速成为各行业不可或缺的…

StarRocks 物化视图刷新流程及原理

前段时间给 StarRocks 的物化视图新增了一个特性,那也是我第一次接触 StarRocks,因为完全不熟悉这个数据库,所以很多东西都是从头开始了解概念。 为了能顺利的新增这个特性(具体内容可以见后文),我需要把整个物化视图的流程串联一遍,于是便有了这篇文章。 在开始之前简单…