记录编号 577220 评测结果 AAAAAAAAAA
题目名称 [HDOJ 3686]交通实时查询系统 最终得分 100
用户昵称 Gravataryuan 是否通过 通过
代码语言 C++ 运行时间 0.164 s
提交时间 2022-10-26 21:40:49 内存使用 13.47 MiB
显示代码纯文本
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 40001,M = 400001;
  4.  
  5. struct Edge {
  6. int from,to,nxt;
  7. }edge[M],edge2[M];
  8.  
  9. int tot,cnt,head[N],head2[N],num;
  10.  
  11. void add(int u,int v) {
  12. edge[++tot].from = u;
  13. edge[tot].to = v;
  14. edge[tot].nxt = head[u];
  15. head[u] = tot;
  16. }
  17.  
  18. void add2(int u,int v) {
  19. edge2[++cnt].from = u;
  20. edge2[cnt].to = v;
  21. edge2[cnt].nxt = head2[u];
  22. head2[u] = cnt;
  23. }
  24.  
  25. int dfn[N],low[N],dfnc,n,m,zhan[N],top;
  26.  
  27. void tarjan(int now,int fa) {//找点双联通分量,缩点,构造圆方树
  28. dfn[now] = low[now] = ++dfnc;//初始化
  29. zhan[++top] = now;//当前结点入栈
  30. for(int i = head[now]; i != -1; i = edge[i].nxt) {
  31. int succ = edge[i].to;//now-->succ后继
  32. if(!dfn[succ]) {//未访问
  33. tarjan(succ,now);
  34. low[now] = min(low[now],low[succ]);
  35. if(dfn[now]<= low[succ]) {//now为割点
  36. num++;//新点,方点
  37. add2(num,now),add2(now,num);//now和方点连边
  38. while(1) {//弹出栈中的所有点,这些点属于同一点双
  39. int x = zhan[top--];
  40. add2(x,num),add2(num,x);//分别和方点连边
  41. if(x == succ) break;
  42. }
  43. }
  44. }
  45. else if(succ!= fa) {//非搜索树边
  46. low[now] = min(low[now],dfn[succ]);
  47. }
  48. }
  49. }
  50.  
  51. int f[N][22],dep[N];
  52. bool vis[N];
  53.  
  54. void dfs(int now,int fa){
  55. vis[now] = 1;//访问标记
  56. dep[now] = dep[fa]+1;//深度
  57. f[now][0] = fa;//2^0辈祖先
  58. for(int i = 1; (1<<i) <= dep[now]; i++)//为倍增求LCA准备
  59. {//求now的2^i辈祖先:now的2^(i-1)辈祖先的2^(i-1)辈祖先
  60. f[now][i] = f[f[now][i-1]][i-1];//爸爸的爸爸叫爷爷
  61. }
  62. for(int i = head2[now]; i!= -1; i = edge2[i].nxt) {
  63. int succ = edge2[i].to;
  64. if(succ!= fa && !vis[succ]) {
  65. dfs(succ,now);
  66. }
  67. }
  68. }
  69.  
  70. int LCA(int x,int y) {//树上倍增求LCA
  71. if(dep[x]>dep[y]) swap(x,y);
  72. int len = dep[y]-dep[x],k = 0;
  73. while(len) {
  74. if(len&1) y = f[y][k];
  75. k++,len >>= 1;
  76. }
  77. if(x == y) return x;
  78. for(int i = 20;i >= 0;i--) {
  79. if(f[x][i] == f[y][i]) continue;
  80. x = f[x][i],y = f[y][i];//同时上跳2^i步
  81. }
  82. return f[x][0];
  83. }
  84.  
  85. int solve(int x,int y) {
  86. return (dep[x]+dep[y]-2*dep[LCA(x,y)])/2-1;
  87. }//圆方树:圆点和方点交替出现,故需/2排除方点
  88.  
  89. int main() {
  90. freopen("traffic_query.in", "r", stdin);
  91. freopen("traffic_query.out", "w", stdout);
  92. while(scanf("%d%d",&n,&m)!= EOF && (n|m))
  93. {//多组数据
  94. memset(head,-1,sizeof(head));
  95. memset(head2,-1,sizeof(head2));
  96. memset(edge,0,sizeof(edge));
  97. memset(edge2,0,sizeof(edge2));
  98. memset(dfn,0,sizeof(dfn));
  99. memset(low,0,sizeof(low));
  100. memset(f,0,sizeof(f));
  101. memset(dep,0,sizeof(dep));
  102. memset(zhan,0,sizeof(zhan));
  103. memset(vis,0,sizeof(vis));
  104. tot = 0,cnt = 0,dfnc = 0,top = 0,num = n;
  105. for(int i = 1;i<= m;i++) {
  106. int u,v;
  107. scanf("%d%d",&u,&v);
  108. add(u,v); add(v,u);
  109. }
  110. for(int i = 1;i<= n;i++)
  111. {//对点双缩点并构造圆方树
  112. if(!dfn[i]) tarjan(i,-1);
  113. }
  114. for(int i = 1;i<= num;i++)
  115. {//dfs求结点i的深度和2^x辈祖先
  116. if(!vis[i]) dfs(i,0);
  117. }
  118. int q;
  119. scanf("%d",&q);
  120. while(q--) {
  121. int s,t;
  122. scanf("%d%d",&s,&t);//起始边
  123. //边到边 转化为 点到点
  124. int ans1 = solve(edge[s*2].from,edge[t*2].from);
  125. int ans2 = solve(edge[s*2].from,edge[t*2].to);
  126. int ans3 = solve(edge[s*2].to,edge[t*2].from);
  127. int ans4 = solve(edge[s*2].to,edge[t*2].to);
  128. int ans = max(max(ans1,ans2),max(ans3,ans4));
  129. printf("%d\n",ans);
  130. }
  131. }
  132. return 0;
  133. }