【题解】道路相遇
\(\text{Solution:}\)
题意就是对于 \((u,v)\) 找有多少个必经点。
必经边就很简单了,直接上边双缩点。这里我们用圆方树解决必经点问题。
考虑到圆方树的性质:方点和圆点一定是相邻的,没有一个圆点连圆点,也没有一个方点连方点。
而对于两点的必经点,就是这条路径上的割点个数,也就是方点个数。
那方点个数不就恰好是路径长度除以 \(2\) 再 \(-1\) 吗,然后加上题目要求的原来的两个点就完成了。
注意这题卡常:一个是空间开大,一个是在 tarjan 的时候就进行建图,避免使用 vector ,还有注意空间别开小,这里提示空间开小导致了 MLE.
还有,链一类的东西也是有圆方树的,每相邻两个点构成点双,并且除了度为 \(1\) 的节点都是割点,它们可以满足圆方相邻的树结构。
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int head[N],Head[N],tot,tto,n,m;
struct E{int nxt,to;}e[N],edge[N<<1];
inline void link(int x,int y,int w=0){
if(w){
edge[++tto]=(E){Head[x],y};
Head[x]=tto;
return;
}
e[++tot]=(E){head[x],y};
head[x]=tot;
}
int dfn[N],low[N],dfstime,top,st[N],dep[N],Q;
int node,siz[N],son[N],ttop[N],pa[N];
void dfs1(int x,int fa){
siz[x]=1;pa[x]=fa;
dep[x]=dep[fa]+1;
for(int i=Head[x];i;i=edge[i].nxt){
int j=edge[i].to;
if(j==fa)continue;
dfs1(j,x);
siz[x]+=siz[j];
if(siz[j]>siz[son[x]])son[x]=j;
}
}
void dfs2(int u,int t){
ttop[u]=t;
if(!son[u])return;
dfs2(son[u],t);
for(int i=Head[u];i;i=edge[i].nxt){
int j=edge[i].to;
if(j==son[u]||j==pa[u])continue;
dfs2(j,j);
}
}
inline void write(int x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
inline int Min(int x,int y){return x<y?x:y;}
void tarjan(int x,int root){
st[++top]=x;
low[x]=dfn[x]=++dfstime;
if(x==root&&!head[x]){
++node;
link(x,node,1);
link(node,x,1);
return;
}
int ch=0;
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(!dfn[j]){
ch++;
tarjan(j,root);
low[x]=Min(low[x],low[j]);
if(low[j]>=dfn[x]){
int vex=-1;
++node;
do{
vex=st[top--];
link(node,vex,1);
link(vex,node,1);
}while(vex!=j);
link(node,x,1);
link(x,node,1);
}
}
else low[x]=Min(low[x],dfn[j]);
}
}
int LCA(int x,int y){
while(ttop[x]!=ttop[y]){
if(dep[ttop[x]]<dep[ttop[y]])swap(x,y);
x=pa[ttop[x]];
}
return dep[x]<dep[y]?x:y;
}
inline int read(){
int s=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch)){
s=s*10-'0'+ch;
ch=getchar();
}
return s;
}
int main(){
n=read();m=read();
for(int i=1;i<=m;++i){
int x=read(),y=read();
link(x,y);link(y,x);
}
node=n;
for(int i=1;i<=n;++i)if(!dfn[i])top=0,tarjan(i,i);
for(int i=1;i<=node;++i){
if(!dep[i]){
dfs1(i,0);
dfs2(i,i);
}
}
Q=read();
while(Q--){
int u=read(),v=read();
int L=LCA(u,v);
write((dep[u]+dep[v]-dep[L]-dep[L])/2+1);
putchar('\n');
}
return 0;
}