• 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)的,这样就能很快算出根了。

  • 相关阅读:
    day11课堂小结 函数作用域
    猜年龄函数版day10作业
    函数day10课堂小结
    day07作业
    文件处理day09
    编码day08
    默写
    day07课堂小结
    day06作业
    const与define应用上该怎么取舍
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/10385873.html
Copyright © 2020-2023  润新知