zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Linux 下的类似Windows下Everything的搜索工具
    windows和linux环境下制作U盘启动盘
    程序调试手段之gdb, vxworks shell
    LeetCode 1021. Remove Outermost Parentheses (删除最外层的括号)
    LeetCode 1047. Remove All Adjacent Duplicates In String (删除字符串中的所有相邻重复项)
    LeetCode 844. Backspace String Compare (比较含退格的字符串)
    LeetCode 860. Lemonade Change (柠檬水找零)
    LeetCode 1221. Split a String in Balanced Strings (分割平衡字符串)
    LeetCode 1046. Last Stone Weight (最后一块石头的重量 )
    LeetCode 746. Min Cost Climbing Stairs (使用最小花费爬楼梯)
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/12838100.html
Copyright © 2011-2022 走看看