• P4320-道路相遇,P5058-[ZJOI2004]嗅探器【圆方树,LCA】


    两题差不多就一起写了

    P4320-道路相遇

    题目链接:https://www.luogu.com.cn/problem/P4320

    题目大意

    (n)个点(m)条边的一张图,(q)次询问两个点之间路径的必经点数量。

    解题思路

    建出圆方树然后问题就变为询问两个点之间路径的圆点数量,可以直接倍增(LCA)求。当然还有更方便的,因为这两个点是圆点,它们的路径一定是一圆一方,所以答案就是它们直接的路径长度(len/2+1)。也是倍增或者树剖(LCA)就好了。

    时间复杂度(O(nlog n))

    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<stack>
    using namespace std;
    const int N=1e6+10,T=20;
    int n,m,t,dfc,dfn[N],low[N];
    int dep[N],f[N][T+1];
    vector<int> G[N],H[N];
    stack<int> s;
    void tarjan(int x){
        dfn[x]=low[x]=++dfc;
        s.push(x);
        for(int y:G[x])
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
                if(low[y]==dfn[x]){
                    int k;++n;
                    do{
                        k=s.top();s.pop();
                        H[n].push_back(k);
                        H[k].push_back(n);
                    }while(k!=y);
                    H[n].push_back(x);
                    H[x].push_back(n);
                }
            }
            else low[x]=min(low[x],dfn[y]);
        return;
    }
    void dfs(int x,int fa){
        dep[x]=dep[fa]+1;
        for(int y:H[x]){
            if(y==fa)continue;
            dfs(y,x);f[y][0]=x;
        }
        return;
    }
    int LCA(int x,int y){
        if(dep[x]>dep[y])swap(x,y);
        for(int i=T;i>=0;i--)
            if(dep[f[y][i]]>=dep[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()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        tarjan(1);dfs(1,0);
        for(int j=1;j<=T;j++)
            for(int i=1;i<=n;i++)
                f[i][j]=f[f[i][j-1]][j-1];
        scanf("%d",&t);
        while(t--){
            int x,y,p;
            scanf("%d%d",&x,&y);
            p=LCA(x,y);
            printf("%d
    ",(dep[x]+dep[y]-2*dep[p])/2+1);
        }
        return 0;
    }
    

    P5058-[ZJOI2004]嗅探器

    题目链接:https://www.luogu.com.cn/problem/P5058

    题目大意

    一张(n)个点(m)条边的无向图,给出两个点求它们之间编号最小的必经点。

    解题思路

    一组询问所以直接建好圆方树(dfs)就好了,如果多组询问的话也可以做,倍增维护树链最小值就好了。

    这题是一组询问所以随便写(O(n))

    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<stack>
    using namespace std;
    const int N=4e5+10;
    int n,m,dfc,cnt,s,t,dfn[N],low[N];
    vector<int> G[N],H[N];
    stack<int> S;
    void tarjan(int x){
        dfn[x]=low[x]=++dfc;
        S.push(x);
        for(int y:G[x])
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
                if(low[y]==dfn[x]){
                    int k;++cnt;
                    do{
                        k=S.top();S.pop();
                        H[cnt].push_back(k);
                        H[k].push_back(cnt);
                    }while(k!=y);
                    H[cnt].push_back(x);
                    H[x].push_back(cnt);
                }
            }
            else low[x]=min(low[x],dfn[y]);
        return;
    }
    int dfs(int x,int fa){
        int k;
        if(x==t)return n+1;
        for(int y:H[x]){
            if(y==fa)continue;
            if(k=dfs(y,x))
                return min((x==s)?(n+1):x,k);
        }
        return 0;
    }
    int main()
    {
        scanf("%d",&n);cnt=n;
        while(1){
            int x,y;
            scanf("%d%d",&x,&y);
            if(!x&&!y)break;
            G[x].push_back(y);
            G[y].push_back(x);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])
                tarjan(i);
        scanf("%d%d",&s,&t);
        s=dfs(s,0);
        if(s>n||!s)printf("No solution
    ");
        else printf("%d
    ",s);
        return 0;
    }
    
  • 相关阅读:
    带有“全选”的combotree
    combotree(组合树)的使用
    根据权限显示accordion
    accordion(折叠面板)的使用
    js中substr、substring、indexOf、lastIndexOf的用法
    EasyUI中使用自定义的icon图标
    EasyUI tree的三种选中状态
    C# List集合去重使用lambda表达式
    C# DataTable去重,根据列名去重保留其他列
    win10下部署.Net Web项目到IIS10
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14276165.html
Copyright © 2020-2023  润新知