【LCA+最短路】Codeforces - 1051 - F. The Shortest Statement
阿新 • • 發佈:2018-11-10
題目連結<http://codeforces.com/problemset/problem/1051/F>
題意:
給出一張n個點,m條邊的無向連通圖。有q次詢問,每次詢問兩個節點的最短距離。
(1≤n,m≤1e5,m−n≤20,1≤q≤1e5)
題解:
因為m−n≤20,所以圖總體來看就是棵樹。把能夠構成環的點拎出來,構成一個集合p。因為它們的數量也不多,可以對集合p內的每個點都做一次最短路。
對於每次詢問u和v之間的最短路:
- 如果u或者v不在樹上,那麼可以直接輸出答案;
- 如果兩個點都在樹上,那麼有答案有兩種情況:1)樹上LCA求出的距離。2)集合p內的點到u和v的距離和。對於這兩種情況取個最小值即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll N=2e5+7; ll fa[N][20],d[N],dis[N]; ll p[N],edn; ll n,m,c; struct Edge{ ll u,v,w,nxt; Edge(ll u=0,ll v=0,ll w=0,ll nxt=0):u(u),v(v),w(w),nxt(nxt){} }edge[N*2]; void add(ll u,ll v,ll w){ edge[++edn]=Edge(u,v,w,p[u]);p[u]=edn; edge[++edn]=Edge(v,u,w,p[v]);p[v]=edn; } bool vis[N],in[N]; bool ef[N*2]; vector<ll>vc; map<ll,ll>mp; void dfs(ll u){ vis[u]=true; in[u]=true; for(ll i=p[u];~i;i=edge[i].nxt){ if(ef[i]||ef[i^1]) continue; ef[i]=ef[i^1]=true; ll v=edge[i].v; if(vis[v]){ vc.push_back(v); mp[v]=vc.size()-1; continue; } d[v]=d[u]+1; dis[v]=dis[u]+edge[i].w; fa[v][0]=u; dfs(v); } } void init(){ for(ll j=1;(1<<j)<=n;j++){ for(ll i=1;i<=n;i++){ fa[i][j]=fa[fa[i][j-1]][j-1]; } } } ll lca(ll a,ll b){ if(d[a]>d[b])swap(a,b); ll f=d[b]-d[a]; for(ll i=0;(1<<i)<=f;i++){ if((1<<i)&f) b=fa[b][i]; } if(a!=b){ for(ll i=(ll)log2(N);i>=0;i--){ if(fa[a][i]!=fa[b][i]){ a=fa[a][i]; b=fa[b][i]; } } a=fa[a][0]; } return a; } ll dist[50][N]; struct Node{ ll id,val; Node(ll id,ll val):id(id),val(val){} bool operator<(const Node a)const{ return val>a.val; } }; void dij(ll x){ memset(vis,false,sizeof(vis)); ll y=vc[x];dist[x][y]=0; priority_queue<Node>q; q.push(Node(y,0)); while(!q.empty()){ ll u=q.top().id;q.pop(); if(vis[u]) continue; vis[u]=true; for(ll i=p[u];~i;i=edge[i].nxt){ ll v=edge[i].v; if(vis[v]) continue; if(dist[x][v]-dist[x][u]>edge[i].w){ dist[x][v]=dist[x][u]+edge[i].w; q.push(Node(v,dist[x][v])); } } } } int main() { while(scanf("%lld%lld",&n,&m)!=EOF){ memset(p,-1,sizeof(p)),edn=-1; memset(d,0,sizeof(d)); memset(dis,0,sizeof(dis)); memset(vis,false,sizeof(vis)); memset(in,false,sizeof(in)); memset(ef,false,sizeof(ef)); vc.clear();mp.clear(); ll u,v,w; for(ll i=1;i<=m;i++){ scanf("%lld%lld%lld",&u,&v,&w); add(u,v,w); } dfs(1); init(); ll sz=vc.size(); memset(dist,125,sizeof(dist)); for(ll i=0;i<sz;i++) dij(i); scanf("%lld",&c); for(ll i=1;i<=c;i++){ scanf("%lld%lld",&u,&v); if(!in[u]) printf("%lld\n",dist[mp[u]][v]); else if(!in[v]) printf("%lld\n",dist[mp[v]][u]); else{ ll pp=lca(u,v); ll ans=dis[u]+dis[v]-dis[pp]*2; for(ll j=0;j<sz;j++){ if(dist[j][u]==dist[sz][sz]) continue; if(dist[j][v]==dist[sz][sz]) continue; ans=min(ans,dist[j][u]+dist[j][v]); } printf("%lld\n",ans); } } } }