[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; }
星星之火,终将成燎原之势