修路 (build.pas/c/cpp)
问题描述
有 n 个兵营里驻扎着士兵,由于兵营间没有路,士兵不能相互增援。现在工程队每天
可以修建一条连接两个兵营的双向道路。现在指挥官询问,对于一个兵营 u,最早什么时候
可以增援兵营 v,即两个兵营之间有连通的路径。
输入数据
第一行两个整数 N,M,表示兵营的数量和道路的数量,兵营从 1-N 编号。
接下来 m 行,每行两个整数 x,y,表示会按顺序修建兵营 x 和兵营 y 之间的道路,所有道
路都是双向的,数据可能存在重边。
接下来一行一个整数 Q,表示有 Q 个询问。
接下来 Q 行每行两个整数 u,v,表示询问兵营 u 最早在什么时候能出兵增援兵营 v。
输出数据
每行一个整数 ti,表示对于第 i 个询问,最早在 ti 天后能满足要求。若始终无法满足,
则输出-1。
输入样例 1
4 3
1 2
1 3
2 3
3
1 2
2 3
1 4
输出样例 1
1
2
-1
数据范围
对于 30%的数据,1
≤
≤
N
≤
≤
100,1
≤
≤
M
≤
≤
1000,1
≤
≤
Q
≤
≤
1000。
对于 100%的数据,1
≤
≤
N
≤
≤
10^5,1
≤
≤
M
≤
≤
10^6,1
≤
≤
Q
≤
≤
10^5。
题解
- 刚拿到这一道题有点手足无措,第一点想到的是要求天数最短并且有重边,那么可以确定是最小生成树,并且是Kruskal算法。
- 接着想了一个多小时,想出来查询的两个点必定经过他们的公共祖先,那么只需要在倍增算法搜索到公共祖先的同时找到最大的所经过边的权值,可以用dp来做。
- 剩下的就是代码实现问题了(然而我很可怜的调一个越界挑了一个下午TAT)
附上代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define MAXN 100010
using namespace std;
void ff(){
freopen("build.in","r",stdin);
freopen("build.out","w",stdout);
}
struct Edge{
int from,to,v;
};
vector <Edge>ss;
vector <int> G[MAXN];
int n,m,Q,large;
//并查集
int father[MAXN],rank1[MAXN];
int find(int x){
if(x!=father[x]){
father[x]=find(father[x]);
}
return father[x];
}
void merge(int x,int y){
x=find(x),y=find(y);
if(rank1[x]<rank1[y]){
father[x]=father[y];
}else{
father[y]=father[x];
if(rank1[x]==rank1[y]){
rank1[x]++;
}
}
}
//搜索建树同时LCA
bool visited[MAXN];
int d[MAXN],p[MAXN][20],tt[MAXN][20];
void dfs(int u,int deepth){
d[u]=deepth;visited[u]=true;
int size=G[u].size();
for (int i=0;i<size;i++){
int v=ss[G[u][i]].to;
if(!d[v]){
p[v][0]=u;
tt[v][0]=ss[G[u][i]].v;
dfs(v,deepth+1);
}
}
}
void make(){
for (int j=1;(1<<j)<=n;j++){
for (int i=1;i<=n;i++){
p[i][j]=p[p[i][j-1]][j-1];
tt[i][j]=max(tt[i][j-1],tt[p[i][j-1]][j-1]);//关键存最大边的方程
}
}
}
int lca(int x,int y){//LCA搜索
int ans1=0,ans2=0;
if(d[x]<d[y]){
int temp=x;
x=y;
y=temp;
}
for (int i=large;i>=0;i--){
if(d[y]<=d[x]-(1<<i)){
ans1=max(ans1,tt[x][i]);
x=p[x][i];
}
}
if(x==y) return ans1;
for (int i=large;i>=0;i--){
if(p[x][i]!=p[y][i]){
ans1=max(ans1,tt[x][i]);
ans2=max(ans2,tt[y][i]);
x=p[x][i];
y=p[y][i];
}
}
ans1=max(ans1,tt[x][0]);
ans2=max(ans2,tt[y][0]);
return max(ans1,ans2);
}
int main(){
ff();
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) father[i]=i;
for (int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
int x=find(a),y=find(b);
if(x!=y){//读入同时进行Kruskal的建树
merge(x,y);
ss.push_back((Edge){a,b,i});
G[a].push_back(ss.size()-1);
ss.push_back((Edge){b,a,i});
G[b].push_back(ss.size()-1);
}
}
memset(visited,false,sizeof(visited));
memset(tt,0,sizeof(tt));
for (int i=1;i<=n;i++){
if(!visited[i]){
p[i][0]=i;
dfs(i,1);
}
}
large=0;
while(1<<large<=n) large++;
large--;
make();
scanf("%d",&Q);
for (int i=1;i<=Q;i++){
int a,b;
scanf("%d%d",&a,&b);
if(find(a)!=find(b)){
printf("-1\n");
continue;
}
printf("%d\n",lca(a,b));
}
}