题目描述

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)]\times2)/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)]\times2)/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)]\times2)/2-1\)

因此最终的结果为\(ans(xx,yy)=(dep[xx]+dep[yy]-dep[LCA(xx,yy)]\times2)/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\n",ans);
}
}
return 0;
}

Traffic Real Time Query System 圆方树+LCA的更多相关文章

  1. 【luogu4320】道路相遇 (圆方树 + LCA)

    Description ​ 给你一张\(~n~\)个点\(~m~\)条边的无向图,保证无重边无自环, 共\(~q~\)组询问求\(~x~\)到\(~y~\)的路径上必经的点数. Solution ​ ...

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

    两题差不多就一起写了 P4320-道路相遇 题目链接:https://www.luogu.com.cn/problem/P4320 题目大意 \(n\)个点\(m\)条边的一张图,\(q\)次询问两个 ...

  3. HDU 3686 Traffic Real Time Query System (图论)

    HDU 3686 Traffic Real Time Query System 题目大意 给一个N个点M条边的无向图,然后有Q个询问X,Y,问第X边到第Y边必需要经过的点有多少个. solution ...

  4. 圆方树简介(UOJ30:CF Round #278 Tourists)

    我写这篇博客的原因 证明我也是学过圆方树的 顺便存存代码 前置技能 双联通分量:点双 然后就没辣 圆方树 建立 新建一个图 定义原图中的所有点为圆点 对于每个点双联通分量(只有两个点的也算) 建立一个 ...

  5. 【BZOJ2125】最短路(仙人掌,圆方树)

    [BZOJ2125]最短路(仙人掌,圆方树) 题面 BZOJ 求仙人掌上两点间的最短路 题解 终于要构建圆方树啦 首先构建出圆方树,因为是仙人掌,和一般图可以稍微的不一样 直接\(tarjan\)缩点 ...

  6. 【CF487E】Tourists(圆方树)

    [CF487E]Tourists(圆方树) 题面 UOJ 题解 首先我们不考虑修改,再来想想这道题目. 我们既然要求的是最小值,那么,在经过一个点双的时候,走的一定是具有较小权值的那一侧. 所以说,我 ...

  7. Codeforces 487E Tourists [广义圆方树,树链剖分,线段树]

    洛谷 Codeforces 思路 首先要莫名其妙地想到圆方树. 建起圆方树后,令方点的权值是双联通分量中的最小值,那么\((u,v)\)的答案就是路径\((u,v)\)上的最小值. 然而这题还有修改, ...

  8. UOJ#30/Codeforces 487E Tourists 点双连通分量,Tarjan,圆方树,树链剖分,线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ30.html 题目传送门 - UOJ#30 题意 uoj写的很简洁.清晰,这里就不抄一遍了. 题解 首先建 ...

  9. CF487E Tourists 圆方树、树链剖分

    传送门 注意到我们需要求的是两点之间所有简单路径中最小值的最小值,那么对于一个点双联通分量来说,如果要经过它,则一定会经过这个点双联通分量里权值最小的点 注意:这里不能缩边双联通分量,样例\(2\)就 ...

随机推荐

  1. zabbix3.2升级3.4报错Database error

    摘要: zabbix3.2版本升级到zabbix3.4版本后打开页面报错,报错内容如下 Database errorThe frontend does not match Zabbix databas ...

  2. zabbix 监控进程,端口

    环境介绍 操作系统:centos 7.4 zabbix版本:zabbix server 3.4.7 客户端:zabbix-agent 3.4.7 监控进程:mysqld 监控端口:3306 tcp 进 ...

  3. 微信小程序 简单获取屏幕视口高度

    由于小程序的宽度是固定的 750rpx,我们可以先用wx.getSystemInfo 来获取可使用窗口的宽高(并非rpx),结合750rpx的宽度算出比例,再用比例来算出高度 let that = t ...

  4. git提交代码托管平台流程

    首先先安装git git官网 ---- https://git-scm.com/ 下载好傻瓜式安装即可 安装好过后,再桌面任意空白区域右键,看到以下两个选项即为安装成功 一般都用第二个选项也就是 Gi ...

  5. 如何将H5一键部署到托管服务中

    随着各个大型App都推出了自己的小游戏平台,游戏也越来越受到开发者的关注.Cocos Creator是一个完整的游戏开发解决方案,包含了轻量高效的跨平台游戏引擎,以及能让你更快速开发游戏所需要的各种图 ...

  6. Git中的core.autocrlf选项

    项目的开发环境为Windows,在Linux环境下编译,使用Git进行版本控制. 在安装好Git和TortoiseGit后,从远端clone,遇到一个奇怪的问题,Shell脚本中的LF总是被替换成了C ...

  7. 列表、元组、字典和简单if语句【python实验1】

    第一次实验报告: 学生姓名 总成绩 tom 90 jack 89 john 96 kate 86 peter 100 实验内容3-1 建立两个列表分别对学生的姓名和总成绩信息进行存储 name=['t ...

  8. 获取随机User-Agent的请求头

    ''' 获取随机User-Agent的请求头 ''' import random #用户代理User-Agent列表 USER_AGENTS = [ "Mozilla/5.0 (Macint ...

  9. c++的两个冒号::四个点是什么意思,什么作用呢?

    c++的两个冒号::四个点是什么意思,什么作用呢? 双冒号(::)用法 (1)表示“域操作符”例:声明了一个类A,类A里声明了一个成员函数void f(),但没有在类的声明里给出f的定义,那么在类外定 ...

  10. 11.实战交付一套dubbo微服务到k8s集群(4)之使用Jenkins进行持续构建交付dubo服务的提供者

    1.登录到jenkins,新建一个项目 2.新建流水线 3.设置保留的天数及份数 4. 添加参数 # 参数 . name: git_repo type: string description: 项目在 ...