无聊的LCA

来源:互联网 发布:sqlserver教材 编辑:程序博客网 时间:2024/05/21 19:48

无聊的时候颓了一发LCA
首先,它有四种解法:
辣鸡的Tarjan,有时候很辣鸡的倍增,很快很快一点都不靠玄学树剖,还有凡是树剖做得到的它都做得到的LCT(尽管要加个log)

辣鸡的Tarjan

为什么很辣鸡:
因为它离线,所以辣鸡。
就算跑得再快,用不了当然辣鸡。
提前知道所有的(u,v),然后搞个并查集,dfs一遍,每次把孩子的集并入父亲,看它的对是否已并入,并入则记录答案,没并入继续跑。

int Find(int x){return fa[x]==x?x:fa[x]=Find(fa[x]);}void Union(int x,int y) {    fx=Find(x),fy=Find(y);    if(fx==fy) return;    fa[fy]=fx;}void Dfs(int x,int f) {    f[x]=1;    for(int i=from[x];i;i=q[i].next)        if(f[q[i].to])            ans[q[i].numb]=Find(q[i].to);    for(int i=head[x];i>=1;i=e[i].next)        if(!f[e[i].to]) {Dfs(e[i].to);Union(x,e[i].to);}}

有时候很辣鸡的倍增

为什么说“有时候很辣鸡”,因为它是“严格的nlog2n”,所以……有时会玄学地被卡(不过也许是我写了假的倍增)

void dfs(int x,int f) {    pre[x][0]=f;dep[x]=dep[f]+1;    for(int i=1;i<=logs&&pre[x][i];i++) pre[x][i]=pre[pre[x][i-1]][i-1];    for(int i=head[x];i;i=nxt[i]) if(to[i]!=f) dfs(to[i],x);}int LCA(int a,int b) {    if(dep[a]>dep[b]) swap(a,b);    for(int i=logs;i>=0;i--)        if(dep[pre[b][i]]>=dep[a]) b=pre[b][i];    if(a==b) return a;    for(int i=logs;i>=0;i--)        if(pre[a][i]!=pre[b][i]) a=pre[a][i],b=pre[b][i];    return pre[a][0];}

“要是有人发现了错误请在下面指出”

最快的树剖

原理仍然不再累述,我只想说,虽然理论复杂度是nlog2n,但是跑起来快得飞起。请轻重链剖分。当然长链剖分被卡也不关我的事
论如何把dfs缩到最短

int dfs(int x,int f) {    fa[x]=f;size[x]=1;dep[x]=dep[f]+1;    for(int i=head[x];i;i=nxt[i])        if(to[i]!=f) {            dfs(to[i],x);size[x]+=size[to[i]];            if(size[to[i]]>size[son[x]]) son[x]=to[i];        }}int dfs2(int x,int f) {    top[x]=f;if(son[x]) dfs2(son[x],f);    for(int i=head[x];i;i=nxt[i])        if(to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]);}int LCA(int x,int y) {    for(;top[x]!=top[y];dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]]);    return dep[x]<dep[y]?x:y;}

凡是树剖能干的它都能干的LCT

Splay的常数比log2n还要恐怖,但是代码好像比树剖难打一点。算了不嫌弃了……你卡一卡还是能完美地A掉的。

struct Link_Cut_Tree {    int c[N][2],fa[N],sumv[N],val[N],q[N],top[N];    bool rev[N];    void pd(int x) {        int &l=c[x][0],&r=c[x][1];        if(rev[x]) {rev[x]^=1;rev[r]^=1;rev[l]^=1;swap(r,l);}    }    bool isrt(int x) {return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}    void rot(int x) {        int y=fa[x],z=fa[y],l=(c[y][1]==x),r=l^1;        if(!isrt(y)) c[z][c[z][1]==y]=x;        fa[c[x][r]]=y;fa[y]=x;fa[x]=z;        c[y][l]=c[x][r];c[x][r]=y;    }        void splay(int x) {        int top=0;q[++top]=x;        for(int i=x;!isrt(i);i=fa[i]) q[++top]=fa[i];        for(;top;pd(q[top--]));        for(int y,z;!isrt(x);rot(x))            if(!isrt(y=fa[x])) c[y][0]==x^c[z=fa[y]][0]==y?rot(x):rot(y);    }    int access(int x){        int last=0;        for(int t=0;x;t=x,x=fa[x])splay(x),c[x][1]=t,last=x;        return last;    }    void makeroot(int x) {access(x);splay(x);rev[x]^=1;}    void link(int x,int y) {makeroot(x);fa[x]=y;}}T;int main(){    int n=read(),m=read(),r=read();    for(int u,v,i=1;i<n;i++){        u=read(),v=read();        T.link(u,v);    }    T.makeroot(r);    for(int u,v,ans;m--;){        u=read(),v=read();        T.access(u);ans=T.access(v);        printf("%d\n",ans);    }    return 0;}

留个小BUFF

区间LCA求法<代码至今没有调过去>

嘛嘛嘛感谢这位m0哦,还有RMQ理论上是可以O(n)解决的但是我就不解决了(跑)

void dfs(int u,int d) {    int i,v,w;    for(vis[u]=true,ver[++tot]=u,fr[u]=tot,R[tot]=d,i=head[u];i;k=e[i].nxt)        if(!vis[e[i].v])            dir[v=e[k].v]=dir[u]+e[i].w,dfs(v,d+1),ver[++tot]=u;R[tot]=d;}int LCA(int u ,int v) {    int x=fr[u],y=fr[v];    if(x>y) swap(x,y);    int K=(int)(log((double)(y-x+1))/log(2.0));    int a=dp[x][K],b=dp[y-_pow[K]+1][K];    if(R[a] < R[b]) return ver[a];    else return ver[b];}
0 0