zoukankan      html  css  js  c++  java
  • D5 LCA 最近公共祖先

    第一题: POJ 1330 Nearest Common Ancestors POJ 1330

    这个题可不是以1为根节点,不看题就会一直wa呀;

    加一个找根节点的措施;

    #include<algorithm>
    #include<bitset>
    #include<cctype>
    #include<cerrno>
    #include<clocale>
    #include<cmath>
    #include<complex>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<deque>
    #include<exception>
    #include<fstream>
    #include<functional>
    #include<limits>
    #include<list>
    #include<map>
    #include<iomanip>
    #include<ios>
    #include<iosfwd>
    #include<iostream>
    #include<istream>
    #include<ostream>
    #include<queue>
    #include<set>
    #include<sstream>
    #include<stack>
    #include<stdexcept>
    #include<streambuf>
    #include<string>
    #include<utility>
    #include<vector>
    #include<cwchar>
    #include<cwctype>
    #define N 50010
    using namespace std;
    int f[N][20],d[N],dist[N],lin[N*2],b[N],root;
    inline int read() {
        int s = 0, w = 1;
        char ch = getchar();
        while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
        while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
        return s * w;
    }
    struct gg
    {
        int y,v,next;
    }a[N<<1];
    int T,n,m,tot,t;
    queue<int> q;
    void add(int x,int y)
    {
        a[++tot].y=y;
        a[tot].next=lin[x];
        lin[x]=tot;
    }
    void bfs(int x)
    {
        q.push(x);d[x]=1;
        while(q.size())
        {
            int x=q.front();q.pop();
            for(int i=lin[x];i;i=a[i].next)
            {
                int y=a[i].y;
                if(d[y]) continue;
                d[y]=d[x]+1;
                f[y][0]=x;
                for(int j=1;j<=t;j++)
                    f[y][j]=f[f[y][j-1]][j-1];
                q.push(y);
            }
        }
    }
    int lca(int x,int y)
    {
        if(d[x]>d[y]) swap(x,y);
        for(int i=t;i>=0;i--)
            if(d[f[y][i]]>=d[x]) y=f[y][i];
        if(x==y) return x;
        for(int i=t;i>=0;i--)
            if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    int main()
    {
        T=read();
        while(T--)
        {
            memset(b,0,sizeof(b));
            memset(f,0,sizeof(f));
            queue<int> q;
            n=read();
            t=(int)(log(n)/log(2))+1;
            for(int i=1;i<=n;i++) lin[i]=d[i]=0;
            tot=0;
            for(int i=1;i<n;i++)
            {
                int x,y,z;
                x=read();y=read();
                b[y]++;
                add(x,y);
            }
            for(int i=1;i<=n;i++) 
            {
                if(b[i]==0)
                {
                    root=i;
                    break;
                }
            }    
            bfs(root);
            int x,y;
            x=read();y=read();
            cout<<lca(x,y)<<endl;    
        }
        return 0;
    }
    View Code

    第二题:HDU 2586

    加了边权的lca模板;

    关键最近距离为dis【i】+dis【j】-2*dis【lca(x,y)】;

    #include<algorithm>
    #include<bitset>
    #include<cctype>
    #include<cerrno>
    #include<clocale>
    #include<cmath>
    #include<complex>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<deque>
    #include<exception>
    #include<fstream>
    #include<functional>
    #include<limits>
    #include<list>
    #include<map>
    #include<iomanip>
    #include<ios>
    #include<iosfwd>
    #include<iostream>
    #include<istream>
    #include<ostream>
    #include<queue>
    #include<set>
    #include<sstream>
    #include<stack>
    #include<stdexcept>
    #include<streambuf>
    #include<string>
    #include<utility>
    #include<vector>
    #include<cwchar>
    #include<cwctype>
    
    #define N 50010
    using namespace std;
    int f[N][20],d[N],dist[N],lin[N*2];
    inline int read() {
        int s = 0, w = 1;
        char ch = getchar();
        while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
        while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
        return s * w;
    }
    struct gg
    {
        int y,v,next;
    }a[N<<1];
    int T,n,m,tot,t;
    queue<int> q;
    void add(int x,int y,int z)
    {
        a[++tot].y=y;
        a[tot].next=lin[x];
        lin[x]=tot;
        a[tot].v=z;
    }
    
    void bfs()
    {
        q.push(1);d[1]=1;
        while(q.size())
        {
            int x=q.front();q.pop();
            for(int i=lin[x];i;i=a[i].next)
            {
                int y=a[i].y;
                if(d[y]) continue;
                d[y]=d[x]+1;
                dist[y]=dist[x]+a[i].v;
                f[y][0]=x;
                for(int j=1;j<=t;j++)
                    f[y][j]=f[f[y][j-1]][j-1];
                q.push(y);
            }
        }
    }
    int lca(int x,int y)
    {
        if(d[x]>d[y]) swap(x,y);
        for(int i=t;i>=0;i--)
            if(d[f[y][i]]>=d[x]) y=f[y][i];
        if(x==y) return x;
        for(int i=t;i>=0;i--)
            if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    int main()
    {
        T=read();
        while(T--)
        {
            queue<int> q;
            n=read();m=read();
            t=(int)(log(n)/log(2))+1;
            for(int i=1;i<=n;i++) lin[i]=d[i]=0;
            tot=0;
            for(int i=1;i<n;i++)
            {
                int x,y,z;
                x=read();y=read();z=read();
                add(x,y,z);add(y,x,z);
            }
            bfs();
            for(int i=1;i<=m;i++)
            {
                int x,y;
                x=read();y=read();
                cout<<dist[x]+dist[y]-2*dist[lca(x,y)]<<endl;
            }
            
        }
        return 0;
    }
    View Code

    第三题:BZOJ 1787

    对三个节点两两求LCA会有2种情况: 1 均相同:答案即为此LCA: 2 有1个LCA与其他的不同:答案为此LCA。

    #include<bits/stdc++.h>
    #define N 500001
    using namespace std;
    int f[N][20],d[N],dist[N],lin[N*2],ans;
    inline int read() {
        int s = 0, w = 1;
        char ch = getchar();
        while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
        while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
        return s * w;
    }
    struct gg
    {
        int y,v,next;
    }a[N<<1];
    int T,n,m,tot,t;
    queue<int> q;
    void add(int x,int y)
    {
        a[++tot].y=y;
        a[tot].next=lin[x];
        lin[x]=tot;
    }
    
    void bfs()
    {
        q.push(1);d[1]=1;
        while(q.size())
        {
            int x=q.front();q.pop();
            for(int i=lin[x];i;i=a[i].next)
            {
                int y=a[i].y;
                if(d[y]) continue;
                d[y]=d[x]+1;
                f[y][0]=x;
                for(int j=1;j<=t;j++)
                    f[y][j]=f[f[y][j-1]][j-1];
                q.push(y);
            }
        }
    }
    int lca(int x,int y)
    {
        ans=0;
        if(d[x]>d[y]) swap(x,y);
        for(int i=t;i>=0;i--)
            if(d[f[y][i]]>=d[x]) y=f[y][i];
        if(x==y) return x;
        for(int i=t;i>=0;i--)
            if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        return ans=f[x][0];
    }
    int dis(int x,int y)
    {
        int t=lca(x,y);
        return d[x]+d[y]-2*d[t];
    }
    int main()
    {
        queue<int> q;
        n=read();m=read();
        t=(int)(log(n)/log(2))+1;
        for(int i=1;i<=n;i++) lin[i]=d[i]=0;
        tot=0;
        for(int i=1;i<n;i++)
        {
            int x,y;
            x=read();y=read();
            add(x,y);add(y,x);
        }
        bfs();
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            x=read();y=read();z=read();
            int xx,yy,zz;
            int p1=lca(x,y),p2=lca(x,z),p3=lca(y,z),t;
            if(p1==p2) t=p3;
            else if(p2==p3) t=p1;
            else t=p2;
            int ans=dis(x,t)+dis(y,t)+dis(z,t);
            cout<<t<<' '<<ans<<endl;
        }
        return 0;
    }
    View Code

    来自石神的代码;跑的很快,推荐;

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=5e5+10;
    const int inf=0x7fffffff;
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1,ch=getchar();
        while (!isdigit(ch)) ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
    inline void add(int x,int y)
    {
        ver[++len]=y,Next[len]=head[x],head[x]=len;
    }
    queue<int>q;
    int d[maxn],f[maxn][21],vis[maxn],t;
    inline void bfs(int root)
    {
        q.push(root);
        d[root]=1;
        while (!q.empty())
        {
            int x=q.front();
            q.pop();
            for (int i=head[x];i;i=Next[i])
            {
                int y=ver[i];
                if (d[y]) continue;
                d[y]=d[x]+1;
                f[y][0]=x;
                for (int j=1;j<=t;++j)
                    f[y][j]=f[f[y][j-1]][j-1];
                q.push(y);
            }
        }
    }
    inline int lca(int x,int y)
    {
        if (d[x]>d[y]) swap(x,y);
        for (int i=t;i>=0;--i)
            if (d[f[y][i]]>=d[x]) y=f[y][i];
        if (x==y) return x;
        for (int i=t;i>=0;--i)
            if (f[x][i]!=f[y][i])
                x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    int main()
    {
        int n,m,id,ans;read(n);read(m);
        t=log2(n*1.0);
        memset(d,0,sizeof(d));
        memset(f,0,sizeof(f));
        memset(vis,0,sizeof(vis));
        memset(head,0,sizeof(head));
        len=0;
        for (int i=1;i<n;++i)
        {
            int x,y;read(x);read(y);
            add(x,y);add(y,x);
        }
        bfs(1);
        for (int i=1;i<=m;++i)
        {
            int x,y,z;read(x);read(y);read(z);
            int l1=lca(x,y),l2=lca(x,z),l3=lca(y,z),ans=inf,tmp,id;
            int q1=lca(l1,z),q2=lca(l2,y),q3=lca(l3,x);
            tmp=d[x]+d[y]-d[l1]+d[z]-(d[q1]<<1);
            if (tmp<ans)
                ans=tmp,id=l1;
            tmp=d[x]+d[z]-d[l2]+d[y]-(d[q2]<<1);
            if (tmp<ans)
                ans=tmp,id=l2;
            tmp=d[y]+d[z]-d[l3]+d[x]-(d[q3]<<1);
            if (tmp<ans)
                ans=tmp,id=l3;
            printf("%d %d
    ",id,ans);
        }
        return 0;
    }
    View Code

    第四题:UVA11354 Bond 

    一类例题:LUOGU UVA 11354 最小生成树+倍增求LCA

    NOIP 2013 货车运输 最大生成树+倍增求LCA

    这个题:最小瓶颈树。 倍增维护边权最大值。

    一份代码解决两个问题,代码注释的代码改一改;

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=6e5+10;
    const int inf=0x3f3f3f3f;
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1,ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    struct Edge
    {
        int x,y,z,next;
    }G[maxn<<1],A[maxn<<1];//G[]是最大生成树的图
    int n,m,head[maxn],len;
    inline void add(int x,int y,int z)
    {
        G[++len].y=y,G[len].z=z,G[len].next=head[x],head[x]=len;
    }
    int fa[maxn];
    inline int get(int x)
    {
        if (x==fa[x]) return x;
        return fa[x]=get(fa[x]);
    }
    inline bool cmp(Edge a,Edge b)
    {
    //    return a.z>b.z;
        return a.z<b.z;
    }
    inline void Kruskal()
    {
        sort(A+1,A+m+1,cmp);
        for (int i=1;i<=n;++i)
            fa[i]=i;
        for (int i=1;i<=m;++i)
        {
            int x=get(A[i].x),y=get(A[i].y);
            if (x!=y)
            {
                fa[y]=x;
                add(A[i].x,A[i].y,A[i].z);
                add(A[i].y,A[i].x,A[i].z);
            }
        }
    }
    int d[maxn],f[maxn][21],w[maxn][21];//fa[]表示并查集中的父节点,f[][]表示树上的父节点,w[][]表示最大载重
    inline void dfs(int x)
    {
        for (int i=1;i<=20;++i)//LCA初始化
        {
            f[x][i]=f[f[x][i-1]][i-1];
    //        w[x][i]=min(w[x][i-1],w[f[x][i-1]][i-1]);
            w[x][i]=max(w[x][i-1],w[f[x][i-1]][i-1]);
        }
        for (int i=head[x];i;i=G[i].next)
        {
            int y=G[i].y;
            if (d[y]) continue;
            d[y]=d[x]+1;//计算深度
            f[y][0]=x;//储存父节点
            w[y][0]=G[i].z;//储存到父节点的权值
            dfs(y);
        }
    }
    inline int lca(int x, int y)
    {
        if (get(x)!=get(y)) return -1;//不连通,输出-1
    //    int ans=inf;
        int ans=0;
        if (d[x]>d[y]) swap(x,y);//保证y节点更深
        for (int i=20;i>=0;--i)//将y节点上提到于x节点相同深度
            if (d[f[y][i]]>=d[x])
            {
    //            ans=min(ans,w[y][i]);//更新最大载重(最小边权)
                ans=max(ans,w[y][i]);
                y=f[y][i];//修改y位置
            }
        if (x==y) return ans;//如果位置已经相等,直接返回答案
        for (int i=20;i>=0;--i)//寻找公共祖先
            if (f[x][i]!=f[y][i])
            {
    //            ans=min(ans,min(w[x][i], w[y][i]));//更新最大载重(最小边权)
                ans=max(ans,max(w[x][i],w[y][i]));
                x=f[x][i],y=f[y][i];//修改x,y位置
            }
    //  ans=min(ans,min(w[x][0],w[y][0]));//更新此时x,y到公共祖先最大载重,f[x][0], f[y][0]即为公共祖先
        ans=max(ans,max(w[x][0],w[y][0]));
        return ans;
    }
    int main()
    {
        int flag=0;
        while (scanf("%d %d",&n,&m)!=EOF)
        {
            if (flag) printf("
    ");
            else flag=1;
            memset(f,0,sizeof(f));
            memset(w,0,sizeof(w));
            memset(d,0,sizeof(d));
            memset(head,0,sizeof(head));
            len=0;
            for (int i=1;i<=m;++i)
                read(A[i].x),read(A[i].y),read(A[i].z);
            Kruskal();
            for (int i=1;i<=n;++i)//dfs收集信息
                if (!d[i])
                {
                    d[i]=1;
                    dfs(i);
                    f[i][0]=i;
    //                w[i][0]=inf;
                    w[i][0]=-inf;
                }
            int q;
            read(q);
            while (q--)
            {
                int x,y;
                read(x);read(y);
                printf("%d
    ",lca(x,y));
            }
        }
        return 0;
    }
    View Code

    第五题:BZOJ 1977 【模板】严格次小生成树[BJWC2010]

    luogu 4180
    BZOJ 1977

    先求一次最小生成树,然后枚举哪些非树边,找到以非树边两端点 在树上路径中最大的一条边,将这条边加入树,形成一个环,那么删掉 此环上的一条边,就会从新出现一棵生成树。

    如何高效的去找要删去的边?

    倍增LCA 倍增维护3个值,father维护LCA,f 维护路径上的最大值,g维护路 径上的严格次大值。

    对于删环上的边,如果添加进来的边与环上最大值不同,那么直接 删换上最大值,如果与最大值相同,就必须删次大值。

    #include <algorithm>
    #include <cctype>
    #include <cmath>
    #include <complex>
    #include <cstdio>
    #include <cstring>
    #include <deque>
    #include <functional>
    #include <list>
    #include <map>
    #include <iomanip>    
    #include <iostream>
    #include <queue>
    #include <set>
    #include <stack>
    #include <string>
    #include <vector>
    #define N 100001
    #define M 300001
    #define inf 0x7fffffff
    #define ll long long
    using namespace std;
    int n,m,tot,cnt,mn=inf;
    ll ans;
    int father[N],lin[N],deep[N],f[N][17],d1[N][17],d2[N][17];
    inline int read() {
        int s = 0, w = 1;
        char ch = getchar();
        while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
        while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
        return s * w;
    }
    void put(int x)
    {
        if(x==0){putchar('0');putchar('
    ');return;}
        if(x<0){putchar('-');x=-x;}
        int num=0;char ch[25];
        while(x) ch[++num]=x%10+'0',x/=10;
        while(num) putchar(ch[num--]);
        putchar('
    ');
    }
    struct data {
        int x,y,v;
        bool bl;
    } a[M];
    struct edge {
        int to,next,v;
    } e[N*2];
    bool cmp(data a,data b) {
        return a.v<b.v;
    }
    void add(int u,int v,int w) {
        e[++cnt].to=v;
        e[cnt].next=lin[u];
        e[cnt].v=w;
        lin[u]=cnt;
    }
    int find(int x) {
        return x==father[x]?x:find(father[x]);
    }
    void dfs(int x,int fs) {
        for(int i=1; i<=16; i++) {
            if(deep[x]<(1<<i))    break;
            f[x][i]=f[f[x][i-1]][i-1];
            d1[x][i]=max(d1[x][i-1],d1[f[x][i-1]][i-1]);
            if(d1[x][i-1]==d1[f[x][i-1]][i-1])
                d2[x][i]=max(d2[x][i-1],d2[f[x][i-1]][i-1]);
            else {
                d2[x][i]=min(d1[x][i-1],d1[f[x][i-1]][i-1]);
                d2[x][i]=max(d2[x][i-1],d2[x][i]);
                d2[x][i]=max(d2[x][i],d2[f[x][i-1]][i-1]);
            }
        }
        for(int i=lin[x]; i; i=e[i].next)
            if(e[i].to!=fs) {
                f[e[i].to][0]=x;
                d1[e[i].to][0]=e[i].v;
                deep[e[i].to]=deep[x]+1;
                dfs(e[i].to,x);
            }
    }
    int lca(int x,int y) {
        if(deep[x]<deep[y])    swap(x,y);
        int t=deep[x]-deep[y];
        for(int i=0; i<=16; i++)
            if((1<<i)&t)x=f[x][i];
        for(int i=16; i>=0; i--) {
            if(f[x][i]!=f[y][i]) {
                x=f[x][i];
                y=f[y][i];
            }
        }
        if(x==y)return x;
        return f[x][0];
    }
    void cal(int x,int fs,int v) {
        int mx1=0,mx2=0;
        int t=deep[x]-deep[fs];
        for(int i=0; i<=16; i++) {
            if(t&(1<<i)) {
                if(d1[x][i]>mx1) {
                    mx2=mx1;
                    mx1=d1[x][i];
                }
                mx2=max(mx2,d2[x][i]);
                x=f[x][i];
            }
        }
        if(mx1!=v)mn=min(mn,v-mx1);
        else mn=min(mn,v-mx2);
    }
    void tp(int t,int v) {
        int x=a[t].x,y=a[t].y,f=lca(x,y);
        cal(x,f,v);
        cal(y,f,v);
    }
    int main() {
        n=read();m=read();
        for(int i=1; i<=n; i++)
            father[i]=i;
        for(int i=1; i<=m; i++)
            a[i].x=read(),a[i].y=read(),a[i].v=read();
        sort(a+1,a+m+1,cmp);
        for(int i=1; i<=m; i++) {
            int p=find(a[i].x),q=find(a[i].y);
            if(p!=q) {
                father[p]=q;
                ans+=a[i].v;
                a[i].bl=1;
                add(a[i].x,a[i].y,a[i].v);
                add(a[i].y,a[i].x,a[i].v);
                tot++;
                if(tot==n-1)break;
            }
        }
        dfs(1,0);
        for(int i=1; i<=m; i++)
            if(!a[i].bl)    tp(i,a[i].v);
        printf("%lld",ans+mn);
        return 0;
    }
    View Code

    第六题:跳跳棋

    BZOJ 2144
    LUOGU 1852

    为了方便研究跳法,我们把棋子按坐标大小排序后设为a, b, c。

    每次都有三种跳法: 1 b往左跳 2 b往右跳 3 离b近的往里跳(远的不允许跳,会越过两个棋子)

    从只有两种跳法的所有状态出发,就可以到达任意一种状态。

    因为 两边往中间跳实际上是一种状态的还原。 对于每一个状态(x, y, z):中间的向外面跳为(2x − y, x, z)或 者(x, z, 2z − y),设为左结点和右结点。

    所以我们就可以把题意转化一下,即第一问为两种状态是否有lca, 而第二种则是问两种状态在树上的距离。我们可以采用类似倍增的方法 跳lca,来求出答案。

    首先,我们应该判断两个状态可不可以互达。

    要做到这一点,实际上就是看两个状态所在树的根是不是相同就行 了。

    怎么样算出这个根呢?

    令b − a = d1, c − b = d2,不妨设d1 < d2,那么a, b两点可以一直向 右,每次移动d1距离,直到d2 − k × d1 ≤ d1为止,这几乎是一个取模运 算。

    1 d1|d2 : 最后剩下d1距离,移动d2|d1 − 1步。

    2 剩下d2 mod d1距离,移动[d1d2|
    步。 而根据与其类似的辗转相除的过程的复杂度,这样计算的复杂度 是O(logd)的,这样就能很快算出根了。

  • 相关阅读:
    各国语言缩写列表,各国语言缩写-各国语言简称,世界各国域名缩写
    How to see log files in MySQL?
    git 设置和取消代理
    使用本地下载和管理的免费 Windows 10 虚拟机测试 IE11 和旧版 Microsoft Edge
    在Microsoft SQL SERVER Management Studio下如何完整输出NVARCHAR(MAX)字段或变量的内容
    windows 10 x64系统下在vmware workstation pro 15安装macOS 10.15 Catelina, 并设置分辨率为3840x2160
    在Windows 10系统下将Git项目签出到磁盘分区根目录的方法
    群晖NAS(Synology NAS)环境下安装GitLab, 并在Windows 10环境下使用Git
    使用V-2ray和V-2rayN搭建本地代理服务器供局域网用户连接
    windows 10 专业版安装VMware虚拟机碰到的坑
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/10385873.html
Copyright © 2011-2022 走看看