每日一题 7月7日 最短路 01最短路+树的LCA
题目链接:https://ac.nowcoder.com/acm/problem/19814
题目大意:
思路:
m最大为n+100,并且连通。我们可以假设开始一棵树。有q个查询u-v的最短路。然后添加了100条边。那么最短路可以发生变化的一定经过了这些边。假设这些边为x-y。那么经过了x点。我们以x为起点跑一个最短路,那么a-b的最短路可能为ans[i]=min(ans[i], dis[a]+dis[b])。当然对于每个查询我们不能去枚举一次100条边。而是用100条边上的点跑完最短路都去更新下q个查询。复杂度O(100qlogn)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
struct node{
int x;
int y;
int i;
}ans[1000050];
vector<node> G[1000050];
int vis[1000050], visE[1000050], ds[1000050], pw[1000050]={1};
struct LCA{
int d[1000050], fa[1000050][22], lg[1000050];
void init(int n, int root){//预处理
for(int i = 1; i <= n; ++i){
lg[i] = lg[i-1] + (1 << lg[i-1] == i);
}
dfs(root, 0);
}
void dfs(int now, int father){
vis[now]=1;
ds[now]=ds[father]+1;
fa[now][0]=father; d[now]=d[father]+1;
for(int i=1; i<=lg[d[now]]; ++i){
fa[now][i]=fa[fa[now][i-1]][i-1];
}
for(auto x: G[now]){
if(x.x!=father&&!vis[x.x]){
visE[x.i]=1;
dfs(x.x, now);
}
}
}
int lca(int x, int y){//LCA
if(d[x]<d[y]) swap(x, y);
while(d[x]>d[y]){
x=fa[x][lg[d[x]-d[y]]-1];
}
if(x==y) return x;
for(int k=lg[d[x]]-1; k>=0; --k){
if(fa[x][k]!=fa[y][k]){
x=fa[x][k], y=fa[y][k];
}
}
return fa[x][0];
}
}lca;
queue<int> q;
int dis[1000050];
void bfs(int u, int Q){
memset(dis, -1, sizeof(dis));
q.push(u), dis[u]=0;
while(!q.empty()){//跑一次最短路
int to=q.front(); q.pop();
for(auto x: G[to]){
if(dis[x.x]==-1){
dis[x.x]=dis[to]+1;
q.push(x.x);
}
}
}
for(int i=1; i<=Q; i++){//更新每个查询
ans[i].i=min(ans[i].i, dis[ans[i].x]+dis[ans[i].y]);
}
}
int form[1000050], to[1000050];
int main(){
for(int i=1; i<=30; i++){
pw[i]=pw[i-1]*2;
}
int n, m, x, y; scanf("%d%d", &n, &m);
for(int i=1; i<=m; i++){
scanf("%d%d", &x, &y);
form[i]=x, to[i]=y;
G[x].push_back(node{y,0, i});
G[y].push_back(node{x,0, i});
}
lca.init(n+3, 1);//从图中得到一颗树
int q; scanf("%d", &q);
for(int i=1; i<=q; i++){
scanf("%d%d", &x, &y);
ans[i]=node{x, y, ds[x]+ds[y]-2*ds[lca.lca(x, y)]};
}
for(int i=1; i<=m; i++){
if(!visE[i]){//非树边
bfs(form[i], q);
}
}
for(int i=1; i<=q; i++){
printf("%d\n", ans[i].i);
}
return 0;
}
