[Codeforces 1051F] The Shortest Statement 解题报告(树+最短路)

题目链接:

https://codeforces.com/contest/1051/problem/F

题目大意:

给出一张$n$个点,$m$条边的带权无向图,多次询问,每次给出$u,v$,要求输出$u$到$v$的最短距离

$1<=n<=m<=10^5,m-n<=20$

题解:

显然我们要从$m-n<=20$入手,发现这张图非常的稀疏,所以按照套路我们先随便搞一棵生成树(和kruskal的步骤差不多只是去掉了排序)。

当询问两个点$u,v$的最短距离时我们先只考虑树上的点,显然我们可以预处理出到根节点的距离$O(qlogn)$的搞。

然后考虑非树边,我们称非树边的端点为特殊点,特殊点的个数小于等于40,显然不在树上的最短路径肯定至少经过一个特殊点,所以我们对每个特殊点跑一次dijkstra,然后每次再枚举一下特殊点更新答案即可

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<vector>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;

const int N=2e5+15;
const ll inf=1e17;
int n,m,tot,k;
int u[N],v[N],tmp[N],fa[N][25],f[N],head[N],d[N];
ll dd[50][N],dep[N],w[N];
struct EDGE
{
    int to,nxt;ll w;
}edge[N<<1];
struct NODE
{
    int to;ll w;
};
vector <NODE> g[N];
set <int> p;
struct node
{
    int now;ll dis;
};
bool operator < (node x,node y) {return x.dis>y.dis;}
inline ll read()
{
    char ch=getchar();
    ll s=0,f=1;
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
int find(int x)
{
    if (x!=f[x]) f[x]=find(f[x]);
    return f[x];
}
void add(int x,int y,ll w)
{
    edge[++tot]=(EDGE){y,head[x],w};
    head[x]=tot;
}
void dij(int s)
{
    ++k;
    for (int i=1;i<=n;i++) dd[k][i]=inf;
    dd[k][s]=0;
    priority_queue <node> q; 
    q.push((node){s,0});
    while (!q.empty())
    {
        node e=q.top();q.pop();
        int now=e.now;
        if (dd[k][now]!=e.dis) continue;
        for (int i=0;i<g[now].size();i++)
        {
            int y=g[now][i].to;
            if (dd[k][y]>dd[k][now]+g[now][i].w)
            {
                dd[k][y]=dd[k][now]+g[now][i].w;
                q.push((node){y,dd[k][y]});
            }
        }
    }
}
void dfs(int x,int pre)
{
    for (int i=1;i<=24;i++) 
    {
        if (d[x]<(1<<i)) break;
        fa[x][i]=fa[fa[x][i-1]][i-1];
    }
    for (int i=head[x];i;i=edge[i].nxt)
    {
        int y=edge[i].to;
        if (y==pre) continue;
        fa[y][0]=x;
        dep[y]=dep[x]+edge[i].w;
        d[y]=d[x]+1;
        dfs(y,x);
    }
}
int lca(int x,int y)
{
    if (d[x]<d[y]) swap(x,y);
    for (int i=24;i>=0;i--) if (d[fa[x][i]]>=d[y]) x=fa[x][i];
    if (x==y) return x;
    for (int i=24;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int main()
{    
    n=read();m=read();
    for (int i=1;i<=m;i++)
    {
        u[i]=read();v[i]=read();w[i]=read();
        g[u[i]].push_back((NODE){v[i],w[i]});
        g[v[i]].push_back((NODE){u[i],w[i]});
    }
    for (int i=1;i<=n;i++) f[i]=i;
    for (int i=1;i<=m;i++)
    {
        int U=find(u[i]),V=find(v[i]);
        if (U!=V)
        {
            f[U]=V;
            tmp[i]=1;
        }
    }
    for (int i=1;i<=m;i++)
    {
        if (tmp[i]) {add(u[i],v[i],w[i]);add(v[i],u[i],w[i]);}//printf("qq%d %d\n",u[i],v[i]);
        else p.insert(u[i]),p.insert(v[i]);
    }
    for (set<int>::iterator it=p.begin();it!=p.end();it++) dij((*it));
    d[0]=-1;dfs(1,-1);
    //printf("dd%d\n",fa[2][0]);
    int q=read();
    while (q--)
    {
        int x=read(),y=read();
        int LCA=lca(x,y);
        //printf("LL%d\n",LCA);
        ll mi=dep[x]+dep[y]-2*dep[LCA];
        //printf("LL%lld\n",mi);
        for (int i=1;i<=k;i++)
        {
            mi=min(mi,dd[i][x]+dd[i][y]);
        }
        printf("%lld\n",mi);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xxzh/p/9765154.html