1. 程式人生 > >【LCA+最短路】Codeforces - 1051 - F. The Shortest Statement

【LCA+最短路】Codeforces - 1051 - F. The Shortest Statement

題目連結<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);
            }
        }
    }
}