• Traffic Real Time Query System 圆方树+LCA


    题目描述

    City C is really a nightmare of all drivers for its traffic jams. To solve the traffic problem, the mayor plans to build a RTQS (Real Time Query System) to monitor all traffic situations. City C is made up of N crossings and M roads, and each road connects two crossings. All roads are bidirectional. One of the important tasks of RTQS is to answer some queries about route-choice problem. Specifically, the task is to find the crossings which a driver MUST pass when he is driving from one given road to another given road.

    Input

    There are multiple test cases.
    For each test case:
    The first line contains two integers N and M, representing the number of the crossings and roads.
    The next M lines describe the roads. In those M lines, the i th line (i starts from 1)contains two integers X i and Y i, representing that road i connects crossing X i and Y i (X i≠Y i).
    The following line contains a single integer Q, representing the number of RTQs.
    Then Q lines follows, each describing a RTQ by two integers S and T(S≠T) meaning that a driver is now driving on the roads and he wants to reach roadt . It will be always at least one way from roads to roadt.
    The input ends with a line of “0 0”.
    Please note that: 0<N<=10000, 0<M<=100000, 0<Q<=10000, 0<X i,Y i<=N, 0<S,T<=M

    Output

    For each RTQ prints a line containing a single integer representing the number of crossings which the driver MUST pass.

    样例

    Sample Input

    5 6
    1 2
    1 3
    2 3
    3 4
    4 5
    3 5
    2
    2 3
    2 4
    0 0
    

    Sample Output

    0
    1
    

    分析

    题意:一个城市有n个路口,m条无向公路。现在要求从第S条路到第T条路必须经过的点有几个。

    这道题和压力那一道题比较像,但是比那一道题要简单一些。

    题目中要求从一条路到另一条路的必经点,我们先不去考虑边,我们先只去考虑点

    如果是点的话那就很好说了,我们用Tarjan对点双进行缩点,使之成为一棵圆方树

    缩点之后的树一定是圆点与方点交替分布的,我们算出两点之间经过的圆点个数就可以了

    要求经过的圆点个数,我们要先求两点之间的距离

    最后再把这个距离除以2,再向下取整减去1就是我们想要的结果

    (ans(xx,yy)=(dep[xx]+dep[yy]-dep[LCA(xx,yy)] imes2)/2-1)

    这个结果是怎么来的呢,我们简单地推导一下

    很显然的是,原先的的边所连接的两个点在新的圆方树中都是圆点

    而且这两个点的的最近公共祖先不是方点就是圆点

    我们设这两个点分别为xx、yy

    1、如果它们的最近公共祖先是圆点

    xx和它的最近公共祖先间圆点的个数=((dep[xx]-dep[LCA(xx,yy)])/2)

    yy和它的最近公共祖先间圆点的个数=((dep[yy]-dep[LCA(xx,yy)])/2)

    因为它们的最近公共祖先作为圆点被计算了两次,所以两式相加后还要减去1

    (ans(xx,yy)=(dep[xx]-dep[LCA(xx,yy)])/2+(dep[yy]-dep[LCA(xx,yy)])/2-1)

    化简得到(ans(xx,yy)=(dep[xx]+dep[yy]-dep[LCA(xx,yy)] imes2)/2-1)

    2、如果它们的最近公共祖先是方点

    xx和它的最近公共祖先间圆点的个数=((dep[xx]-dep[LCA(xx,yy)]-1)/2)

    yy和它的最近公共祖先间圆点的个数=((dep[yy]-dep[LCA(xx,yy)]- 1)/2)

    两式相加

    (ans(xx,yy)=(dep[xx]-dep[LCA(xx,yy)]-1)/2+(dep[yy]-dep[LCA(xx,yy)]-1)/2)

    化简得到(ans(xx,yy)=(dep[xx]+dep[yy]-dep[LCA(xx,yy)] imes2)/2-1)

    因此最终的结果为(ans(xx,yy)=(dep[xx]+dep[yy]-dep[LCA(xx,yy)] imes2)/2-1)

    但是我们要求的是边到边之间经过的圆点的个数,这怎么办呢

    其实我们只要把每条边的两个顶点分别枚举求最大值就可以了

    代码

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int maxd=40005,maxb=400005;
    struct asd{
        int from,to,next;
    }b[maxb],b2[maxb];
    int tot=1,t2=1,head[maxd],h2[maxd],nn;
    void ad(int aa,int bb){
        b[tot].from=aa;
        b[tot].to=bb;
        b[tot].next=head[aa];
        head[aa]=tot++;
    }
    void ad2(int aa,int bb){
        b2[t2].from=aa;
        b2[t2].to=bb;
        b2[t2].next=h2[aa];
        h2[aa]=t2++;
    }
    int dfn[maxd],low[maxd],dfnc;
    int n,m,sta[maxd],top;
    void tarjan(int now,int fa){
        dfn[now]=low[now]=++dfnc;
        sta[++top]=now;
        for(int i=head[now];i!=-1;i=b[i].next){
            int u=b[i].to;
            if(!dfn[u]){
                tarjan(u,now);
                low[now]=min(low[now],low[u]);
                if(dfn[now]<=low[u]){
                    nn++;
                    ad2(nn,now),ad2(now,nn);
                    while(1){
                        int xx=sta[top--];
                        ad2(xx,nn),ad2(nn,xx);
                        if(xx==u) break;
                    }
                }
            } else if(u!=fa){
                low[now]=min(low[now],dfn[u]);
            }
        }
    }
    int f[maxd][22],dep[maxd];
    bool vis[maxd];
    void dfs(int now,int fa){
        vis[now]=1;
                dep[now]=dep[fa]+1;
                f[now][0]=fa;
        for(int i=1;(1<<i)<=dep[now];i++){
            f[now][i]=f[f[now][i-1]][i-1];
        }
        for(int i=h2[now];i!=-1;i=b2[i].next){
            int u=b2[i].to;
            if(u!=fa && !vis[u]){
                dfs(u,now);
            }
        }
    }
    int LCA(int xx,int yy){
        if(dep[xx]>dep[yy]) swap(xx,yy);
        int len=dep[yy]-dep[xx],k=0;
        while(len){
            if(len&1){
                yy=f[yy][k];
            }
            k++,len>>=1;
        }
        if(xx==yy) return xx;
        for(int i=20;i>=0;i--){
            if(f[xx][i]==f[yy][i]) continue;
            xx=f[xx][i],yy=f[yy][i];
        }
        return f[xx][0];
    }
    int solve(int xx,int yy){
        return (dep[xx]+dep[yy]-2*dep[LCA(xx,yy)])/2-1; 
    }
    int main(){
        while(scanf("%d%d",&n,&m)!=EOF && (n|m)){
            memset(head,-1,sizeof(head));
            memset(h2,-1,sizeof(h2));
            memset(&b,0,sizeof(struct asd));
            memset(&b2,0,sizeof(struct asd));
            memset(dfn,0,sizeof(dfn));
            memset(low,0,sizeof(low));
            memset(f,0,sizeof(f));
            memset(dep,0,sizeof(dep));
            memset(sta,0,sizeof(sta));
            memset(vis,0,sizeof(vis));
            tot=1,t2=1,dfnc=0,top=0,nn=n;
            for(int i=1;i<=m;i++){
                int aa,bb;
                scanf("%d%d",&aa,&bb);
                ad(aa,bb);
                ad(bb,aa);
            }
            for(int i=1;i<=n;i++){
                if(!dfn[i]) tarjan(i,-1);
            }
            for(int i=1;i<=nn;i++){
                if(!vis[i]) dfs(i,0);
            }
            int q;
            scanf("%d",&q);
            while(q--){
                int aa,bb;
                scanf("%d%d",&aa,&bb);
                int ans1=solve(b[aa*2].from,b[bb*2].from);
                int ans2=solve(b[aa*2].to,b[bb*2].from);
                int ans3=solve(b[aa*2].from,b[bb*2].to);
                int ans4=solve(b[aa*2].to,b[bb*2].to);
                int ans=max(max(ans1,ans2),max(ans3,ans4));
                printf("%d
    ",ans);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    TCP的三次握手与四次挥手理解及面试题(很全面)
    python解释器锁的理解
    Flask的基本使用、四剑客和配置文件
    Django cache缓存
    xadmin后台管理
    cookies与session
    Java stream流
    Java IO流
    springboot配置文件加载顺序与一些常用配置
    OAuth2.0开放授权
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/12838100.html
Copyright © 2020-2023  润新知