• Codeforces 519E A and B and Lecture Rooms [倍增法LCA]


    题意:

    给你一棵有n个节点的树,给你m次询问,查询给两个点,问树上有多少个点到这两个点的距离是相等的。树上所有边的边权是1。

    思路:

    很容易想到通过记录dep和找到lca来找到两个点之间的距离,然后分情况讨论。

    一开始困扰我的问题是如果lca不是正中间的点,如何在比较低的复杂度的层面上求解中点。

    倍增法lca不光可以在logn的时间复杂度内查询某两个点的lca,还可以实现在logm的时间复杂度能查询某个节点的第m个父亲节点。

    算法的核心是用二进制的运算来实现查询。

    #include<bits/stdc++.h>
    #define N 100050
    using namespace std;
    int n;
    struct edge{
        int id;
        edge *next;
    };
    edge edges[N<<1];
    edge *adj[N];
    int ednum;
    int dep[N],rt[25][N],siz[N];
    inline void add_Edge(int a,int b){
        edge *tmp=&edges[ednum++];
        tmp->id=b;
        tmp->next=adj[a];
        adj[a]=tmp;
    }
    void dfs(int pos,int deep){
        dep[pos]=deep;
        siz[pos]=1;
        for(edge *it=adj[pos];it;it=it->next){
            if(!dep[it->id]){
                rt[0][it->id]=pos;
                dfs(it->id,deep+1);
                siz[pos]+=siz[it->id];
            }
        }
    }
    void prelca(){
        for(int i=1;i<=20;i++){
            for(int j=1;j<=n;j++){
                rt[i][j]=rt[i-1][j]==-1?-1:rt[i-1][rt[i-1][j]];
            }
        }
    }
    int LCA(int u,int v){
        if(dep[u]<dep[v])swap(u,v);
        for(int i=0;i<21;i++){
            if((dep[u]-dep[v])>>i&1){
                u=rt[i][u];
            }
        }
        if(u==v)return u;
        for(int i=19;i>=0;i--){
            if(rt[i][u]!=rt[i][v]){
                u=rt[i][u];
                v=rt[i][v];
            }
        }
        return rt[0][u];
    }
    int jump(int pos,int num){
        for(int i=0;i<21;i++){
            if(num>>i&1){
                pos=rt[i][pos];
            }
        }
        return pos;
    }
    void solve(int u,int v){
        if(u==v){
            printf("%d
    ",n);
            return;
        }
        if(max(dep[u],dep[v])-min(dep[u],dep[v])&1){
            printf("0
    ");
            return;
        }
        int anc=LCA(u,v);
        //printf("anc=%d
    ",anc);
        if(dep[u]==dep[v]){
            int ans=n;
            ans-=siz[jump(u,dep[u]-dep[anc]-1)];
            ans-=siz[jump(v,dep[u]-dep[anc]-1)];
            printf("%d
    ",ans);
        }
        else{
            int l=dep[u]+dep[v]-2*dep[anc];
            if(dep[u]<dep[v])swap(u,v);
            int ans=siz[jump(u,l/2)];
            ans-=siz[jump(u,l/2-1)];
            printf("%d
    ",ans);
        }
    }
    int main()
    {
        scanf("%d",&n);
        memset(rt,-1,sizeof(rt));
        for(int i=1;i<n;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add_Edge(a,b);
            add_Edge(b,a);
        }
        dfs(1,1);
        prelca();
        //printf("*%d
    ",jump(2,0));
        int m;
        scanf("%d",&m);
        for(int  i=1;i<=m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            solve(a,b);
        }
        return 0;
    }
    /*
    5
    1 5
    1 2
    2 3
    2 4
    5
    1 5
    2 5
    1 1
    2 2
    3 4
    */
  • 相关阅读:
    使用greenDAO遇到的问题
    使用greenDAO生成DAO代码
    Spring中Bean的生命周期
    视频弹幕开源库
    最简MacOs10.8安装
    apache-virtual host
    带删除的EditText
    替换默认debug.keystore文件
    Intellij格式化java和xml
    【数据结构】之二叉树的java实现
  • 原文地址:https://www.cnblogs.com/tun117/p/5304642.html
Copyright © 2020-2023  润新知