记录编号 577220 评测结果 AAAAAAAAAA
题目名称 [HDOJ 3686]交通实时查询系统 最终得分 100
用户昵称 Gravataryuan 是否通过 通过
代码语言 C++ 运行时间 0.164 s
提交时间 2022-10-26 21:40:49 内存使用 13.47 MiB
显示代码纯文本
#include <bits/stdc++.h>
using namespace std;
const int N = 40001,M = 400001;

struct Edge {
	int from,to,nxt;
}edge[M],edge2[M];

int tot,cnt,head[N],head2[N],num;

void add(int u,int v) {
	edge[++tot].from = u;
	edge[tot].to = v;
	edge[tot].nxt = head[u];
	head[u] = tot;
}

void add2(int u,int v) {
	edge2[++cnt].from = u;
	edge2[cnt].to = v;
	edge2[cnt].nxt = head2[u];
	head2[u] = cnt;
}

int dfn[N],low[N],dfnc,n,m,zhan[N],top;

void tarjan(int now,int fa) {//找点双联通分量,缩点,构造圆方树
	dfn[now] = low[now] = ++dfnc;//初始化
	zhan[++top] = now;//当前结点入栈
	for(int i = head[now]; i != -1; i = edge[i].nxt) {
		int succ = edge[i].to;//now-->succ后继
		if(!dfn[succ]) {//未访问
			tarjan(succ,now);
			low[now] = min(low[now],low[succ]);
			if(dfn[now]<= low[succ]) {//now为割点
				num++;//新点,方点
				add2(num,now),add2(now,num);//now和方点连边
				while(1) {//弹出栈中的所有点,这些点属于同一点双
					int x = zhan[top--];
					add2(x,num),add2(num,x);//分别和方点连边
					if(x == succ) break;
				}
			}
		}
		else if(succ!= fa) {//非搜索树边
			low[now] = min(low[now],dfn[succ]);
		}
	}
}

int f[N][22],dep[N];
bool vis[N];

void dfs(int now,int fa){
	vis[now] = 1;//访问标记
	dep[now] = dep[fa]+1;//深度
	f[now][0] = fa;//2^0辈祖先
	for(int i = 1; (1<<i) <= dep[now]; i++)//为倍增求LCA准备
	{//求now的2^i辈祖先:now的2^(i-1)辈祖先的2^(i-1)辈祖先
		f[now][i] = f[f[now][i-1]][i-1];//爸爸的爸爸叫爷爷
	}
	for(int i = head2[now]; i!= -1; i = edge2[i].nxt) {
		int succ = edge2[i].to;
		if(succ!= fa && !vis[succ]) {
			dfs(succ,now);
		}
	}
}

int LCA(int x,int y) {//树上倍增求LCA
	if(dep[x]>dep[y]) swap(x,y);
	int len = dep[y]-dep[x],k = 0;
	while(len) {
		if(len&1) y = f[y][k];
		k++,len >>= 1;
	}
	if(x == y) return x;
	for(int i = 20;i >= 0;i--) {
		if(f[x][i] == f[y][i]) continue;
		x = f[x][i],y = f[y][i];//同时上跳2^i步
	}
	return f[x][0];
}

int solve(int x,int y) {
	return (dep[x]+dep[y]-2*dep[LCA(x,y)])/2-1; 
}//圆方树:圆点和方点交替出现,故需/2排除方点

int main() {
	freopen("traffic_query.in", "r", stdin);
	freopen("traffic_query.out", "w", stdout);
	while(scanf("%d%d",&n,&m)!= EOF && (n|m))
	{//多组数据
		memset(head,-1,sizeof(head));
		memset(head2,-1,sizeof(head2));
		memset(edge,0,sizeof(edge));
		memset(edge2,0,sizeof(edge2));
		memset(dfn,0,sizeof(dfn));
		memset(low,0,sizeof(low));
		memset(f,0,sizeof(f));
		memset(dep,0,sizeof(dep));
		memset(zhan,0,sizeof(zhan));
		memset(vis,0,sizeof(vis));
		tot = 0,cnt = 0,dfnc = 0,top = 0,num = n;
		for(int i = 1;i<= m;i++) {
			int u,v;
			scanf("%d%d",&u,&v);
			add(u,v); add(v,u);
		}
		for(int i = 1;i<= n;i++)
		{//对点双缩点并构造圆方树
			if(!dfn[i]) tarjan(i,-1);
		}
		for(int i = 1;i<= num;i++)
		{//dfs求结点i的深度和2^x辈祖先
			if(!vis[i]) dfs(i,0);
		}
		int q;
		scanf("%d",&q);
		while(q--) {
			int s,t;
			scanf("%d%d",&s,&t);//起始边
			//边到边 转化为 点到点
			int ans1 = solve(edge[s*2].from,edge[t*2].from);
			int ans2 = solve(edge[s*2].from,edge[t*2].to);
			int ans3 = solve(edge[s*2].to,edge[t*2].from);
			int ans4 = solve(edge[s*2].to,edge[t*2].to);
			int ans = max(max(ans1,ans2),max(ans3,ans4));
			printf("%d\n",ans);
		}
	}
	return 0;
}