1. 程式人生 > >[Codeforces 1051F] The Shortest Statement 解題報告(樹+最短路)

[Codeforces 1051F] The Shortest Statement 解題報告(樹+最短路)

== pri stat log 最短距離 %d .com turn $1

題目鏈接:

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; }

[Codeforces 1051F] The Shortest Statement 解題報告(樹+最短路)