Time limit: 2 seconds

Memory limit: 1024 megabytes

In ICPCCamp, there are n cities and m (bidirectional) roads between cities. The i-th road is between the

ai-th city and the bi-th city. There may be roads connecting a citie to itself and multiple roads between

the same pair of cities.

Bobo has q travel plans. The i-th plan is to travel from the ui-th city to the vi-th city. He would like

to know the smallest number of roads needed to travel for each plan. It is guaranteed that cities are

connected.

【Input】

The first line contains 3 integers n, m, q (1 ≤ n ≤ 10^5, 0 < m − n < 100, 1 ≤ q ≤ 10^5).

The i-th of the following m lines contains 2 integers ai, bi (1 ≤ ai, bi ≤ n).

The i-th of the last q lines contains 2 integers ui, vi (1 ≤ ui, vi ≤ n)

【Output】

n lines with integers l1, l2, … , ln. li denotes the smallest number of roads travelling from city ui to city vi.

Examples

【standard input 1】

4 5 3

1 2

1 3

1 4

2 3

3 4

2 2

2 3

2 4

【standard output 1】

0

1

2

【standard input 1】

1 2 1

1 1

1 1

1 1

【standard output 1】

0

【题解】



毒题。

讲一下做法:

注意到m最多为n+100最小为n

我们先在m条边中取出n-1条。让它组成一棵树。(用并查集去除多余的边,但是这些多余的边不能删掉要记录下来);

对于每一个询问x,y;先求出它们的最近公共祖先z;然后设某个节点x的深度为dep[x]

则两个点之间的最短路一定是dep[x]+dep[y]-2*dep[z];

但是这只是在去掉了多余的边后的情况。

我们还要把那些边再加进来。

全部都加进来之后。

从加进来的多余的边的端点开始进行spfa。

设询问仍然是x,y;

则再尝试用dis[s][x]+dis[s][y]来更新答案。其中s∈{x|x为多余边的端点}

这里的dis第一维只要开到200多就可以了。

相当于我们在一棵树上进行了一次最短路。

然后再用多余的边上的点作为起点进行最短路。

如果那些多余边上的点是最短路上的点。那么在第二次更新dis[s][x]+dis[s][y]的时候都会涉及到。

可以理解为x->y经过了s这个点的最短路。枚举s。取最小。

添加了边。最短路发生改变。那么新的最短路肯定经过某一条新添加的边的两端点。

很棒的思路。

最后是LCA的实现思路:

盗了几张图:







我发两份代码:

第一份用的是RMQ预处理出LCA的方法。可以AC;

第二份用的是树上倍增的方法,但是TLE。(供学习);

//代码一,可AC
#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
#include <vector> using namespace std; const int MAXN = 4e5 + 10;
const int MAX_REST = 100 + 10;
const int INF = 2100000000; int n, m, q, ans[MAXN], dep[MAXN] = { 0 };
int pr[25];
queue <int> dl;
int dis[MAX_REST * 2][MAXN]; struct bian
{
int x, y;
}; vector <int> a[MAXN], c;
vector < pair<int, int> > rest, b;
int f[MAXN], re = 0;
int p[MAXN][25];
int fi[MAXN], cnt = 0;
int shall[MAXN * 2]; void input(int &num)
{
num = 0;
char c;
do
{
c = getchar();
} while (!isdigit(c));
while (isdigit(c))
{
num = num * 10 + c - '0';
c = getchar();
}
} int ff(int x)
{
if (f[x] == x)
return f[x];
else
f[x] = ff(f[x]);
return f[x];
} int lca(int x, int y)
{
int xx = fi[x];
int yy = fi[y];
if (xx > yy)
swap(xx, yy);
int k = shall[yy - xx + 1];
int a1 = p[xx][k];
int a2 = p[yy - pr[k] + 1][k];//左边和右边都搞下才能覆盖整个区间
if (dep[a1] < dep[a2])
return a1;
else
return a2;
} void dfs(int u, int fa)
{
dep[u] = dep[fa] + 1;
p[++cnt][0] = u;//dfs序列为cnt,长度为2^0的深度最小的节点
fi[u] = cnt;
int len = a[u].size();
for (int i = 0; i <= len - 1; i++)
{
int y = a[u][i];
if (y != fa)
{
dfs(y, u);
p[++cnt][0] = u;
}
}
} void spfa(int s)
{
for (int i = 1; i <= n; i++)//用memset会比较慢
dis[s][i] = INF;
dis[s][c[s]] = 0;
dl.push(c[s]);
while (!dl.empty())
{
int x = dl.front();
dl.pop();
int len = a[x].size();
for (int i = 0; i <= len - 1; i++)
{
int y = a[x][i];
if (dis[s][y] >dis[s][x] + 1)
{
dis[s][y] = dis[s][x] + 1;
dl.push(y);
}
}
}
} int main()
{
//freopen("F:\\rush.txt", "r", stdin);
input(n); input(m); input(q);
for (int i = 1; i <= n; i++)
f[i] = i;
for (int i = 1; i <= m; i++)
{
int x, y;
input(x); input(y);
if (x == y)
continue;
int r1 = ff(x), r2 = ff(y);
if (r1 != r2)
{
f[r1] = r2;
a[x].push_back(y);
a[y].push_back(x);
}
else
rest.push_back(make_pair(x, y));
}
dfs(1, 0);
pr[0] = 1;
for (int i = 1; i <= 24; i++)
pr[i] = pr[i - 1] << 1;
shall[1] = 0;
int now = 1;
for (int i = 2; i <= cnt; i++)//shall[i]表示长度为i的区间需要2的多少次方来覆盖。
{
if (i == pr[now])
{
shall[i] = shall[i - 1] + 1;
now++;
}
else
shall[i] = shall[i - 1];
}
for (int i = 1; pr[i] <= cnt; i++)
for (int j = 1; j + pr[i] - 1 <= cnt; j++)//用RMQ来做
{
int x = p[j][i - 1];
int y = p[j + pr[i - 1]][i - 1];
if (dep[x] < dep[y])
p[j][i] = x;
else
p[j][i] = y;
}
for (int i = 1; i <= q; i++)
{
int x, y;
input(x); input(y);
b.push_back(make_pair(x, y));
ans[i] = dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
int len = rest.size();//把多余的边加入图中
for (int i = 0; i <= len - 1; i++)
{
int x = rest[i].first, y = rest[i].second;
a[x].push_back(y);
a[y].push_back(x);
c.push_back(x);
c.push_back(y);//并取出边的两端点。用来作为spfa的起点
}
len = c.size();
for (int i = 0; i <= len - 1; i++)
spfa(i);
for (int i = 0; i <= q - 1; i++)
{
int x, y;
x = b[i].first, y = b[i].second;
for (int j = 0; j <= len - 1; j++)//需要更新的肯定是那些新添加的点。
{
int s = j;
ans[i + 1] = min(ans[i + 1], dis[s][x] + dis[s][y]);
}
printf("%d\n", ans[i + 1]);
}
return 0;
}
//代码二 会超时 树上倍增
//递推公式:p[i][j] = p[p[i][j-1][j-1];
//p[i][j]表示从i节点往上走2^j个节点后是什么节点。
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 4e5;
const int MAX_REST = 100 + 10;
int n, m, q, ans[MAXN], dep[MAXN] = { 0 };
int pr[20];
queue <int> dl;
int dis[MAX_REST*2][MAXN];
struct bian
{
int x, y;
};
vector <int> a[MAXN],c;
vector < pair<int, int> > rest,b;
int f[MAXN],re = 0,p[MAXN][20];
int ff(int x)
{
if (f[x] == x)
return f[x];
else
f[x] = ff(f[x]);
return f[x];
}
int lca(int x, int y)//倍增求LCA
{
if (dep[x] > dep[y])
swap(x, y);
for (int i = 17; i >= 0; i--)
if (dep[x] <= dep[y] - (pr[i]))
y = p[y][i];
if (x == y)//如果x直接等于y了要直接返回;
return x;
for (int i = 17; i >= 0; i--)
{
if (p[x][i] == p[y][i]) continue;
x = p[x][i], y = p[y][i];
}
return p[x][0];
}
void dfs(int u, int fa)
{
dep[u] = dep[fa] + 1;
p[u][0] = fa;
for (int i = 1; i <= 17; i++)
p[u][i] = p[p[u][i - 1]][i - 1];
int len = a[u].size();
for (int i = 0;i <= len-1;i++)
{
int y = a[u][i];
if (y != fa)
dfs(y, u);
}
}
void spfa(int s)
{
memset(dis[s], 127 / 3, sizeof(dis[s]));
dis[s][c[s]] = 0;
dl.push(c[s]);
while (!dl.empty())
{
int x = dl.front();
dl.pop();
int len = a[x].size();
for (int i = 0; i <= len - 1; i++)
{
int y = a[x][i];
if (dis[s][y] > dis[s][x] + 1)
{
dis[s][y] = dis[s][x] + 1;
dl.push(y);
}
}
}
}
int main()
{
//freopen("F:\\rush.txt", "r", stdin);
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++)
f[i] = i;
for (int i = 1; i <= m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
if (x == y)
continue;
int r1 = ff(x), r2 = ff(y);
if (r1 != r2)
{
f[r1] = r2;
a[x].push_back(y);
a[y].push_back(x);
}
else
rest.push_back(make_pair(x, y));
}
dfs(1, 0);
pr[0] = 1;
for (int i = 1; i <= 17; i++)
pr[i] = pr[i - 1] << 1;
for (int i = 1; i <= q; i++)
{
int x, y;
scanf("%d%d", &x, &y);
b.push_back(make_pair(x, y));
ans[i] = dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
int len = rest.size();
for (int i = 0; i <= len - 1; i++)
{
int x = rest[i].first, y = rest[i].second;
a[x].push_back(y);
a[y].push_back(x);
c.push_back(x);
c.push_back(y);
}
len = c.size();
for (int i = 0; i <= len - 1; i++)
spfa(i);
for (int i = 0; i <= q-1; i++)
{
int x, y;
x = b[i].first, y = b[i].second;
for (int j = 0; j <= len - 1; j++)
{
int s = j;
ans[i+1] = min(ans[i+1], dis[s][x] + dis[s][y]);
}
printf("%d\n", ans[i+1]);
}
return 0;
}

【7.89%】【BNUOJ 52303】Floyd-Warshall的更多相关文章

  1. 【Android】【录音】Android录音--AudioRecord、MediaRecorder

    [Android][录音]Android录音--AudioRecord.MediaRecorder Android提供了两个API用于实现录音功能:android.media.AudioRecord. ...

  2. 【OpenCV新手教程之十二】OpenCV边缘检測:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/25560901 作者:毛星云(浅墨) ...

  3. Python笔记 【无序】 【二】

    序列list() ——把一个可迭代对象[可以是字符串,元组]转化为列表,可不带参数——生成空列表,或者带一个迭代器作为参数tuple() ——可迭代对象转化为元组str(obj) ——把obj对象转换 ...

  4. 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理

    [微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...

  5. 【Python】【容器 | 迭代对象 | 迭代器 | 生成器 | 生成器表达式 | 协程 | 期物 | 任务】

    Python 的 asyncio 类似于 C++ 的 Boost.Asio. 所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束,你可以继续做其他事情,当它结束时,你会得到通知. Asyn ...

  6. day41 python【事物 】【数据库锁】

    MySQL[五] [事物 ][数据库锁]   1.数据库事物 1. 什么是事务  事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.也就是事务具有原子性 ...

  7. POJ 1258 + POJ 1287 【最小生成树裸题/矩阵建图】

    Farmer John has been elected mayor of his town! One of his campaign promises was to bring internet c ...

  8. 程序员需要的各种PDF格式电子书【附网盘免费下载资源地址】

    程序员需要的各种PDF格式电子书[附网盘免费下载资源地址]   各位,请妥善保存,后期还会有更多更新,如果你有不同的书籍资源或者这里没有你要找的书籍,也可以直接留言,后期我们会继续更新~ Java & ...

  9. 【Knockout.js 学习体验之旅】(3)模板绑定

    本文是[Knockout.js 学习体验之旅]系列文章的第3篇,所有demo均基于目前knockout.js的最新版本(3.4.0).小茄才识有限,文中若有不当之处,还望大家指出. 目录: [Knoc ...

随机推荐

  1. 【Codeforces Round #426 (Div. 2) A】The Useless Toy

    [Link]:http://codeforces.com/contest/834/problem/A [Description] [Solution] 开个大小为4的常量字符数组; +n然后余4,-n ...

  2. 洛谷——P3178 [HAOI2015]树上操作

    https://www.luogu.org/problem/show?pid=3178#sub 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种:操作 1 ...

  3. C#打开SDE数据库的几种方式总结

    转自谢灿软件原文 C#打开SDE数据库的几种方式总结 1.通过指定连接属性参数打开数据库 /// <param name="server">数据库服务器名</pa ...

  4. softInputMode- 软件盘监听事件

    软件盘的监听事件,如下 private final OnKeyListener mSubjectKeyListener = new OnKeyListener() { @Override public ...

  5. javascript中函数声明、变量声明以及变量赋值之间的关系与影响

    javascript中函数声明.变量声明以及变量赋值之间的关系与影响 函数声明.变量声明以及变量赋值之间有以下几点共识: 1.所有的全局变量都是window的属性 2.函数声明被提升到范围作用域的顶端 ...

  6. System and method for controlling switching between VMM and VM using enabling value of VMM timer indicator and VMM timer value having a specified time

    In one embodiment, a method includes transitioning control to a virtual machine (VM) from a virtual ...

  7. 华为OJ:字符串反转

    非常easy,逆向输出就好了. import java.util.Scanner; public class convertString { public static void main(Strin ...

  8. sql 高性能存储过程分页

    USE [Lyjjr] GO /****** Object: StoredProcedure [dbo].[P_ViewPage] Script Date: 05/29/2015 17:18:56 * ...

  9. Gmail 收信的一些规则

    Gmail 收信的一些规则 用 apache+php+MDaemon 调试 mail2www 时,发往gmail的邮件失败, 提示: Our system detected an illegal at ...

  10. git仓库搭建

    第一步安装git [root@Centos-node2 ~]# yum -y install git 第二步创建git用户 [root@Centos-node2 ~]# useradd git [ro ...