LCA离线Tarjan,树上倍增入门题
离线Tarjian,来个JVxie大佬博客最近公共祖先LCA(Tarjan算法)的思考和算法实现,还有zhouzhendong大佬的LCA算法解析-Tarjan&倍增&RMQ(其实你们百度lca前两个博客就是。。。)
LCA是最近公共祖先的意思,在上图的话像4和5的最近公共祖先就是2,而4和7的最近公共祖先是1,从某种意义上讲如果不怕超时的话,每次直接暴力搜索是可以找到每两个节点的最近公共祖先的,不过红红的TLE不好看,要想生活过得去,还是得看点AC的绿。
而Tarjan求lca是离线算法,也就是对于所有的询问需要全输入完处理之后再输出答案,而不是输入一组询问输出相应的答案,JVxie大佬的讲解中有很详细的模拟过程,我就不重复了,就简单讲讲自己的理解。
首先,我们需要保存所有询问,像建边一样我们记录下每个节点与其有询问关系的点,像上图的询问4和5的最近公共祖先的话,我们可以在4和5间类似建边一样加上一个关系,用我自己的代码来说就是(我是习惯链式前向星建边)
struct Side{
int v,c,ne;
}S[2*N],Q[2*M];建边:
void adds(int u,int v,int c)
{
S[sn].v=v;
S[sn].c=c;
S[sn].ne=heads[u];
heads[u]=sn++;
}建询问关系:
void addq(int u,int v,int id)
{
Q[qn].v=v;
Q[qn].c=id;
Q[qn].ne=headq[u];
headq[u]=qn++;
}边里面的c就代表边的权值,而询问关系里的c就代表它是第几组询问
然后就是dfs了,我们先对每个点深搜也就是对它的子树遍历一遍,并且用并查集把它的子节点归到它这里,然后当这个点的子树都遍历完后,我们就去遍历和它有询问关系的点,如果和它有询问关系的某个点已经被搜索过了的话,那么某个点当前并查集所归属到的那个点就是他们的最近公共祖先。
因为是按深搜的顺序遍历树,一个节点一开始是归属到它父节点的,如果它的兄弟节点或者它的父节点和它有询问关系的话,那么最近公共节点就是它的父节点,而如果是其他不跟它再同一个分支的节点跟它有询问关系的话,当它的父节点搜索完,它也会跟着它的父节点归属到它的爷爷节点,一直往上归属到两个节点的公共节点。光说太空洞,做题理解,引用大佬推荐的两道题
CODEVS 2370 小机房的树 传送门
中文题,大意就是树上两个节点的最低路径,也就是他们到他们的最近公共祖先的距离,直接上代码了
#include<cstdio>
const int N=,M=;
struct Side{
int v,c,ne;
}S[*N],Q[*M];
int sn,qn,heads[N],headq[N],fa[N],vis[N],dis[N],ans[M];
void init(int n)
{
sn=qn=;
for(int i=;i<=n;i++)
{
fa[i]=i;//并查集,每个节点一开始自己是自己父亲
dis[i]=;
vis[i]=;
heads[i]=headq[i]=-;
}
}
void adds(int u,int v,int c)
{
S[sn].v=v;
S[sn].c=c;
S[sn].ne=heads[u];
heads[u]=sn++;
}
void addq(int u,int v,int id)
{
Q[qn].v=v;
Q[qn].c=id;//记录下它的第几个询问
Q[qn].ne=headq[u];
headq[u]=qn++;
}
int gui(int x){
return fa[x]==x ? x : fa[x]=gui(fa[x]);
}
void bing(int x,int y)
{
int gx=gui(x),gy=gui(y);
if(gx!=gy)
fa[gy]=gx;
}//并查集,我们老师说最简单的算法,嘤嘤嘤
void dfs(int u,int f,int len)
{
dis[u]=len;
for(int i=heads[u];i!=-;i=S[i].ne)
{
int v=S[i].v;
if(v!=f)
{
dfs(v,u,len+S[i].c);
bing(u,v);//把子节点归属到当前节点
vis[v]=;//标记当前节点以及访问过了
}
}
for(int i=headq[u];i!=-;i=Q[i].ne)
{//处理每个和它有关的询问
int v=Q[i].v,id=Q[i].c;
if(vis[v])//当有关系的节点已经被访问过,处理该条询问
ans[id]=dis[u]+dis[v]-*dis[gui(v)];
}
}
int main()
{
int n,m,u,v,c;
scanf("%d",&n);
init(n);
for(int i=;i<n;i++)
{
scanf("%d%d%d",&u,&v,&c);
adds(u,v,c);
adds(v,u,c);
}
scanf("%d",&m);
for(int i=;i<m;i++)
{
scanf("%d%d",&u,&v);
addq(u,v,i);
addq(v,u,i);
//询问也要建双向的,因为不确定谁先访问到
}
dfs(,-,);
for(int i=;i<m;i++)
printf("%d\n",ans[i]);
return ;
}
搞基的虫子~~
为什么前面说最后对树形dp有点理解呢,因为一般不会问最近公共祖先,而是问路径,所以我们需要记录下每个节点到根节点的距离,然后两个节点的距离就是它们到根节点的距离再减去两倍的它们最近公共祖先到根节点的距离(因为这段距离在两个节点到根节点的距离中重复了),比如上图,求4到5的距离的话,我们记录的是根节点,也就是1到4和1到5的距离,然后很明显,在1到4和1到5中都包含了1到2(4和5最近公共祖先)的距离,而我们要求4到5,是不需要1到2这段的距离的。
ZOJ 3195 Design the city 传送门
题目大意也是求树上的最短路径,不过是求三个节点的,也是先上代码
#include<cstdio>
const int N=,M=;
struct Side{
int v,c,ne;
}S[*N],Q[*M];
int sn,qn,heads[N],headq[N],fa[N],vis[N],dis[N],ans[M];
void init(int n)
{
sn=qn=;
for(int i=;i<=n;i++)
{
fa[i]=i;
dis[i]=;
vis[i]=;
heads[i]=headq[i]=-;
}
}
void adds(int u,int v,int c)
{
S[sn].v=v;
S[sn].c=c;
S[sn].ne=heads[u];
heads[u]=sn++;
}
void addq(int u,int v,int id)
{
Q[qn].v=v;
Q[qn].c=id;
Q[qn].ne=headq[u];
headq[u]=qn++;
}
int gui(int x){
return fa[x]==x ? x : fa[x]=gui(fa[x]);
}
void bing(int x,int y)
{
int gx=gui(x),gy=gui(y);
if(gx!=gy)
fa[gy]=gx;
}
void dfs(int u,int f)
{
for(int i=heads[u];i!=-;i=S[i].ne)
{
int v=S[i].v;
if(v!=f)
{
dis[v]=dis[u]+S[i].c;
dfs(v,u);
bing(u,v);
vis[v]=;
}
}
for(int i=headq[u];i!=-;i=Q[i].ne)
{
int v=Q[i].v,id=Q[i].c;
if(vis[v])
ans[id]+=dis[u]+dis[v]-*dis[gui(v)];
}
}
int main()
{
int n,m,u,v,c,is=;
while(~scanf("%d",&n))
{
if(is)
printf("\n");
is=;
init(n);
for(int i=;i<n;i++)
{
scanf("%d%d%d",&u,&v,&c);
adds(u,v,c);
adds(v,u,c);
}
scanf("%d",&m);
int x,y,z;
for(int i=;i<m;i++)
{
ans[i]=;
scanf("%d%d%d",&x,&y,&z);
addq(x,y,i);//每两个点间都要建个询问关系
addq(x,z,i);
addq(y,x,i);
addq(y,z,i);
addq(z,x,i);
addq(z,y,i);
}
dfs(,-);
for(int i=;i<m;i++)
printf("%d\n",ans[i]/);
}
return ;
}
3个点就了不起啊
其实和上题基本都一样,就是在求三个节点时,我们要分别求出两两节点间的最短距离,然后把三个结果相加起来除2就是答案。因为像a,b,c三个节点,我们求出a到b,和b到c,以及a到c的距离,所需要求的距离就会重复了一遍,比如上图求3,4,5的距离,也就是要求3到1,1到2,2到4,2到5这些边,而我们求出3到4是,3到1,1到2,2到4,求出4到5是,4到2,2到5,求出3到5就是,3到1,1到2,2到5,可以看到我们需要求的边都刚好多走了一遍。所以只要用lcd求出两两的最短路,3个加起来除2就是3个节点间的最短路。
夜深了,树上倍增明天再更、树上倍增,我个人感觉它和树链剖分的关系就像树状数组和线段树,有些操作树上倍增做不了,但相同操作,比如求lca,树上倍增的效率更高,而且实现更简单。先放个大佬博客
树上倍增的写法和应用(详细讲解,新手秒懂)Saramanda大佬的,简短精辟。
首先,树上倍增是基于二进制的思想,我们把一个数,比如10我们可以写成10=23+21,也就是1010,那么如果我们保存一个fa[i][j]表示第i个节点的第2j个祖先的话,那么我们求一个节点A的第10个祖先的话,我们就可以先找到它第2个祖先B,然后B再找到它的第8个祖先C,C也就是A的第十个祖先。因为一个节点和它的父亲肯定是在同一条链上,那么它的祖先关系是累加的,就像它的第十个组先就是它第二个祖先的第八个主线,我们把要找的第K个转换为二进制的话,我们就可以根据对位i上有没有1来直接上升到第(1<<i)个祖先,整个算法的复杂度就变成了log级的了,简单的描述就是
找节点u的第K个祖先:
for(int i=0;i<=fn;i++)//fn为最多可能有2fn个祖先,一般不超过20多
{//从fn到0也是可以的
if((1<<i)&k)
u=fa[u][i];
}
比如K是10的话,1001,其实就是在每个1的位置上升也就是呈2(1<<i)倍数增加
既然可以求第k个祖先了,那我们怎么用树上倍增来求lca呢,不同于前面的tarjan算法,树上倍增求lca是在线算法,也就是输入一组询问就可以求相应的结果,所以我们要在dfs时先把fa这个记录祖先的数组还有deep记录深度的数组处理出来。具体操作就是,在每次遍历每个节点u的子树之前,我们先从它第1个祖先开始,依次判断它的第1,2,4,8,2fn-1是否已经存在,已经存在的话,那么fa[u][i]=fa[fa[u][i-1]][i-1],因为2i=2i-1+2i-1嘛,如果第2i-1个祖先都还没存在,那么就不用再向下找第2i,2i+1个等等,代码实现就是
void dfs(int u)
{
for(int i=1;i<=fn;i++)
{
int f=fa[u][i-1];
if(f==-1)//如果第2i-1个祖先不存在,直接可以结束了
break;
fa[u][i]=fa[f][i-1];//否则就是倍增,2i=2i-1+2i-1
}
for(int i=head[u];i!=-1;i=S[i].ne)//然后遍历和它相连的点
{
int v=S[i].v;
if(v!=fa[u][0])//fu[u][0]就是u的父节点
{
fa[v][0]=u;//v的父节点就是u
deep[v]=deep[u]+1;//记录深度
dis[v]=dis[u]+S[i].w;//到根节点的距离
dfs(v);
}
}
}
处理出deep和fa数组后,然后怎么求lca呢,再次用到这个图,我们如果求7和8的lca的话,deep记录的深度,我们从0开始的话,deep也相当于它有多少个祖先,像deep[8]=3,deep[7]=2,我们可以发现7和8不在同一层次,那么我们就要把它们统一层次,也就把8提上来。deep[8]和deep[7]相差了1,我们就找到8的第1个祖先5,这时5和7就同一层次了。同一层次的话,那么它们的最近公共祖先肯定就都是两人第一个相同的第2i个祖先,这个统一层次的过程就是
if(deep[u]<deep[v])//让u是深度大的节点
{
int t=u;
u=v;v=t;
}
int disd=deep[u]-deep[v];//深度差
for(int i=0;i<=fn;i++)//把u提到第disd个祖先
if((1<<i)&disd)
u=fa[u][i];
if(u==v)//同一层次时u和v是同一节点直接可以返回了
return u;
当统一层次而u!=v时,我们继续从fn到0开始往下找,当找到某个不相等的节点时,我们就移到双双移到那个位置。就像i从fn到2时,fa[5][i],fa[7][i]都一直相等,都是不存在的节点(可以用-1代表),而fa[5][1]=fa[7][1]为1这些都不处理,当i=0时,fa[5][0]=2,fa[7][0]=3,两者不相等,所以5移到2,7移到5,最后fa[2][0]或者fa[3][0]也就是1,是5和7的最近公共祖先。
为什么呢?就假如我们找同一层次的a和b的公共祖先,因为i是从fn到0,从2fn到20个祖先的找,当a和b第2i个祖先c和d不相等时,我们就把a移到c,b移到d,因为i是逐渐减小的,那么最后c和d就是a和b最近公共祖先下一层的那两个祖先。
那从0到fn的遍历找第一个相同的祖先可不可以呢?答案是不可以的,因为我们是以20,21,22,23,这样2i级遍历的,那么如果要找的最近公共祖先是a和b的第6个祖先,那么fa[a][2],fa[b][2]不相同,接着就是到fa[a][3]和fa[b][3]了,直接把6跳过了。而从fn到0的话,c=fa[a][2]和d=fa[b][2]不相同,a移到c,b移到d,然后fa[c][1]和fa[d][1]相同跳过,e=fa[c][0]和f=fa[d][0]不同,c再转到e,d转到f,最终a,b最近公共祖先就是fa[e][0]或者fa[f][0],也就是22+20+20
之前的两道题用树上倍增来实现的话就是
#include<cstdio>
const int N=,F=;
struct Side{
int v,w,ne;
}S[*N];
int sn,fn,head[N],dis[N],deep[N],fa[N][F];
void init(int n)
{
sn=;
fn=;
for(int i=;i<=n;i++)
{
head[i]=-;
dis[i]=;
deep[i]=;
for(int j=;j<=fn;j++)
fa[i][j]=-;
}
}
void add(int u,int v,int w)
{
S[sn].v=v;
S[sn].w=w;
S[sn].ne=head[u];
head[u]=sn++;
}
void dfs(int u)
{
for(int i=;i<=fn;i++)
{
int f=fa[u][i-];
if(f==-)
break;
fa[u][i]=fa[f][i-];
}
for(int i=head[u];i!=-;i=S[i].ne)
{
int v=S[i].v;
if(v!=fa[u][])
{
fa[v][]=u;
deep[v]=deep[u]+;
dis[v]=dis[u]+S[i].w;
dfs(v);
}
}
}
int lca(int u,int v)
{
if(deep[u]<deep[v])
{
int t=u;
u=v;v=t;
}//让u为深度较深的那个节点
int disd=deep[u]-deep[v];
for(int i=;i<=fn;i++)
if((<<i)&disd)
u=fa[u][i];
//将u移到和v相同深度的那个祖先
if(u==v)//相同层次时是同一节点
return u;
for(int i=fn;i>=;i--)
if(fa[u][i]!=fa[v][i])
{
u=fa[u][i];
v=fa[v][i];
}
//找最近公共祖先下一层的u,v的祖先
return fa[u][];
}
int main()
{
int n,m,u,v,w;
while(~scanf("%d",&n))
{
init(n);
for(int i=;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dfs();
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&u,&v);
int f=lca(u,v);
printf("%d\n",dis[u]+dis[v]-*dis[f]);
}
}
return ;
}
精力旺盛的虫子
#include<cstdio>
#include<cmath>
const int N=,F=;
struct Side{
int v,w,ne;
}S[*N];
int sn,fn,head[N],dis[N],deep[N],fa[N][F];
void init(int n)
{
sn=;
fn=(int)log(n);
for(int i=;i<=n;i++)
{
head[i]=-;
dis[i]=;
deep[i]=;
for(int j=;j<=fn;j++)
fa[i][j]=-;
}
}
void add(int u,int v,int w)
{
S[sn].v=v;
S[sn].w=w;
S[sn].ne=head[u];
head[u]=sn++;
}
void dfs(int u)
{
for(int i=;i<=fn;i++)
{
int f=fa[u][i-];
if(f==-)
break;
fa[u][i]=fa[f][i-];
}
for(int i=head[u];i!=-;i=S[i].ne)
{
int v=S[i].v;
if(v!=fa[u][])
{
fa[v][]=u;
deep[v]=deep[u]+;
dis[v]=dis[u]+S[i].w;
dfs(v);
}
}
}
int lca(int u,int v)
{
if(deep[u]<deep[v])
{
int t=u;
u=v;v=t;
}
int disd=deep[u]-deep[v];
for(int i=;i<=fn;i++)
if((<<i)&disd)
u=fa[u][i];
if(u==v)
return u;
for(int i=fn;i>=;i--)
if(fa[u][i]!=fa[v][i])
{
u=fa[u][i];
v=fa[v][i];
}
return fa[u][];
}
int main()
{
int n,m,u,v,w,x,is=;
while(~scanf("%d",&n))
{
if(is)
printf("\n");
is=;
init(n);
for(int i=;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dfs();
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d",&u,&v,&x);
int f,ans=;
//计算任意两点间的距离
f=lca(u,v);
ans+=dis[u]+dis[v]-*dis[f];
f=lca(u,x);
ans+=dis[u]+dis[x]-*dis[f];
f=lca(v,x);
ans+=dis[v]+dis[x]-*dis[f];
printf("%d\n",ans/);
}
}
return ;
}
3个点了不起
这里推荐个树上倍增的模板题
Query on a tree IISPOJ - QTREE2
题目大意是,一棵树有两种询问,一直就是问a到b的最短路径,另一种就是问a到b路径上第k个节点是哪个?
前一种操作很简单就是dis[a]+dis[b]-2*dis[lca(a,b)],而第二种的话,我们求出lca(a,b),那么a跟lca(a,b)的深度差就是这条路径上有多少个祖先,所以k-1<=这个深度差的话,那么就是找a的第k-1个祖先,否则就是找b的b跟lca(a,b)的深度差+1-(k-a跟lca(a,b)的深度差)个祖先,
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=,F=;
struct Side{
int v,w,ne;
}S[*N];
char op[];
int sn,fn,head[N],dis[N],deep[N],fa[N][F];
void init(int n)
{
sn=;fn=;
for(int i=;i<=n;i++)
{
dis[i]=;
head[i]=-;
deep[i]=;
for(int j=;j<=fn;j++)
fa[i][j]=-;
}
}
void add(int u,int v,int w)
{
S[sn].w=w;
S[sn].v=v;
S[sn].ne=head[u];
head[u]=sn++;
}
void dfs(int u)
{
for(int i=;i<=fn;i++)
{
int f=fa[u][i-];
if(f==-)
break;
fa[u][i]=fa[f][i-];
}
for(int i=head[u];i!=-;i=S[i].ne)
{
int v=S[i].v;
if(v!=fa[u][])
{
fa[v][]=u;
dis[v]=dis[u]+S[i].w;
deep[v]=deep[u]+;
dfs(v);
}
}
}
int kth(int u,int k)//找u的第k个祖先
{
for(int i=;i<=fn;i++)
if((<<i)&k)
u=fa[u][i];
return u;
}
int lca(int u,int v)//找u,v的最近公共祖先
{
if(deep[u]<deep[v])
swap(u,v);
int disd=deep[u]-deep[v];
u=kth(u,disd);
if(u==v)
return u;
for(int i=fn;i>=;i--)
if(fa[u][i]!=fa[v][i])
{
u=fa[u][i];
v=fa[v][i];
}
return fa[u][];
}
int main()
{
int t,n,u,v,w;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init(n);
for(int i=;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dfs();
while(scanf("%s",op)&&op[]!='O')
{
scanf("%d%d",&u,&v);
int f=lca(u,v);
if(op[]=='K')
{
scanf("%d",&w);
int du=deep[u]-deep[f],dv=deep[v]-deep[f];
//du:u和lca的深度差,dv:v和lca的深度差,
//之所以要-1,+1是因为u,v本身也是路径上的节点
if(w-<=du)
printf("%d\n",kth(u,w-));
else
printf("%d\n",kth(v,dv+-(w-du)));
}
else
printf("%d\n",dis[u]+dis[v]-*dis[f]);
}
printf("\n");
}
return ;
}
倍增倍增倍增
LCA离线Tarjan,树上倍增入门题的更多相关文章
- 近期公共祖先(LCA)——离线Tarjan算法+并查集优化
一. 离线Tarjan算法 LCA问题(lowest common ancestors):在一个有根树T中.两个节点和 e&sig=3136f1d5fcf75709d9ac882bd8cfe0 ...
- [POJ1330]Nearest Common Ancestors(LCA, 离线tarjan)
题目链接:http://poj.org/problem?id=1330 题意就是求一组最近公共祖先,昨晚学了离线tarjan,今天来实现一下. 个人感觉tarjan算法是利用了dfs序和节点深度的关系 ...
- [HDOJ2874]Connections between cities(LCA, 离线tarjan)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2874 这题有不连通的情况,特别注意. 觉得是存query的姿势不对,用前向星存了一遍,还是T…… /* ...
- [HNOI2016]树(可持久化线段树+树上倍增)
[HNOI2016]树(可持久化线段树+树上倍增) 题面 给出一棵n个点的模板树和大树,根为1,初始的时候大树和模板树相同.接下来操作m次,每次从模板树里取出一棵子树,把它作为新树里节点y的儿子.操作 ...
- FJUT 聪明的商人(树上倍增)题解
思路:求树上两点的距离,显然是dep[u] + dep[v] - 2 * dep[lca],用树上倍增去写. 参考:树上倍增的写法和应用(详细讲解,新手秒懂) 代码: #include<set& ...
- [BZOJ3133] [Baltic2013]ballmachine(树上倍增+堆)
[BZOJ3133] [Baltic2013]ballmachine(树上倍增+堆) 题面 有一个装球机器,构造可以看作是一棵树.有下面两种操作: 从根放入一个球,只要下方有空位,球会沿着树滚下.如果 ...
- 两种lca的求法:树上倍增,tarjan
第一种:树上倍增 f[x,k]表示x的2^k辈祖先,即x向根结点走2^k步达到的结点. 初始条件:f[x][0]=fa[x] 递推式:f[x][k]=f[ f[x][k-1] ][k-1] 一次bfs ...
- lca入门———树上倍增法(博文内含例题)
倍增求LCA: father[i][j]表示节点i往上跳2^j次后的节点 可以转移为 father[i][j]=father[father[i][j-1]][j-1] 整体思路: 先比较两个点的深度, ...
- poj 1330 LCA (倍增+离线Tarjan)
/* 先来个倍增 */ #include<iostream> #include<cstring> #include<cstdio> #define maxn 100 ...
随机推荐
- python爬虫实战--抖音
申明&警告: 请在相关网站的许可范围内爬取数据.以免影响网站正常运行, 如果我的文章有触犯权益的地方, 请告知删除. 上一篇爬取知乎的文章基本就是大多数网站的爬取思路了(headers部分其实 ...
- Scalar Queries CodeForces - 1167F (计数,树状数组)
You are given an array $a_1,a_2,…,a_n$. All $a_i$ are pairwise distinct. Let's define function $f(l, ...
- nodejs+gulp+webpack基础知识
nodejs+gulp+webpack基础知识 2019年08月22日 11:49:40 天府云创 阅读数 22 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文 ...
- X-Router软路由设置
一 内网: ip 192.168.0.1 掩码 255.255.255.0 网关 (空) DNS 202.96.128.68(佛山的)手动写入 二 外 ...
- [转载] ReLU和BN层简析
[转载] ReLU和BN层简析 来源:https://blog.csdn.net/huang_nansen/article/details/86619108 卷积神经网络中,若不采用非线性激活,会导致 ...
- js中神奇的东西
简单了解一些js的东西 window.history.go(-1);//历史记录-1,跳转到上一次操作的页面 Location 对象的 replace() 方法用于重新加载当前文档(页面) javas ...
- java技术面试之面试题大全
转载自:http://blog.csdn.net/lijizhi19950123/article/details/77679489 Java 面试知识点总结 本篇文章会对面试中常遇到的Java技术点进 ...
- 《Linux就该这么学》day3
ps:原谅我的书法出自鲁迅的<野草> <Linux就该这么学>书本介绍: 本书是由全国多名红帽架构师(RHCA)基于最新Linux系统共同编写的高质量Linux技术自学教程,极 ...
- 数据总线&地址总线&控制总线
数据总线 (1) 是CPU与内存或其他器件之间的数据传送的通道. (2)数据总线的宽度决定了CPU和外界的数据传送速度. (3)每条传输线一次只能传输1位二进制数据.eg: 8根数据线一次可传送一个8 ...
- C++ 批量打开写入文件
用到了C++17的filesystem 库 说明:这个函数主要是用来处理日志中不同Thread的日志,主要目的是将不同Thread的日志写到不同的文件中 int GetThreadTime(const ...