CF1051F The Shortest Statement
阿新 • • 發佈:2020-08-18
題意簡述
給定一張n個點m條邊的無向圖,滿足m-n<=20,然後進行q次操作,每次給定兩個點,詢問兩點間最短路。
資料範圍:1<=n,m,q<=105。
演算法概述
只看題面顯然是個裸的全源最短路,但是再看資料範圍……顯然不是全源最短路。
所以這時候就需要發揮我們的聰明才智,在題目中找一些特殊性質或者發現一些結論之類的了。
顯然,這道題最特殊的地方在於m-n<=20的限制,相當於在一棵樹上多出了最多21條非樹邊。
對於給定的任意兩個點a,b,最短路無非兩種情況:
1.只經過樹邊,樹上距離簡單容斥一下,d[a]+d[b]-2*d[lca(a,b)];
2.經過非樹邊,那麼我們可以列舉所有非樹邊,對於每條非樹邊(u,v,w),其構成的最短路有且僅有兩種情況,其一是a→u→v→b,其二則是a→v→u→b,兩者取min即可。
那麼我們的演算法框架就出來了:
先求一棵生成樹(以下用Kruskal),然後把非樹邊處理出來。
先在樹上做預處理,求出每個點到根的距離d。
把非樹邊加進去,然後對於所有在非樹邊上的點跑一遍最短路。
最後處理詢問。
時間複雜度O(21*q+42*mlogn)。
參考程式碼
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; typedef long long ll; const int N=1e5+10; struct Edge{ int to,next,w; }edge[N<<1];int idx; int h[N]; void add_edge(int u,int v,int w){edge[++idx]={v,h[u],w};h[u]=idx;} struct E{ int u,v,w; bool operator <(const E &ed)const{ return w<ed.w; } }e[N],spc[N];int cnt; int fa[N]; ll dis[50][N]; int vis[N]; int n,m,q; int dep[N],son[N],siz[N]; int f[N],top[N]; ll d[N]; void dfs1(int p,int father) { f[p]=father; dep[p]=dep[father]+1; siz[p]=1; int max_son=0; for(int i=h[p];~i;i=edge[i].next) { int to=edge[i].to,w=edge[i].w; if(to==father)continue; d[to]=d[p]+w; dfs1(to,p); if(siz[to]>max_son)max_son=siz[to],son[p]=to; siz[p]+=siz[to]; } } void dfs2(int p,int t) { top[p]=t; if(!son[p])return; dfs2(son[p],t); for(int i=h[p];~i;i=edge[i].next) { int to=edge[i].to; if(to==f[p]||to==son[p])continue; dfs2(to,to); } } inline int lca(int x,int y) { while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]])swap(x,y); x=f[top[x]]; } return dep[x]<dep[y]?x:y; } int get(int x) { if(fa[x]==x)return fa[x]; return fa[x]=get(fa[x]); } void dijkstra(int s,ll dist[]) { memset(vis,0,sizeof vis); priority_queue<pair<ll,int> > q; q.push(make_pair(0,s)); dist[s]=0; while(!q.empty()) { int p=q.top().second; q.pop(); if(vis[p])continue; vis[p]=1; for(int i=h[p];~i;i=edge[i].next) { int to=edge[i].to,w=edge[i].w; if(dist[to]>dist[p]+w) { dist[to]=dist[p]+w; q.push(make_pair(-dist[to],to)); } } } } int main() { memset(h,-1,sizeof h); memset(dis,0x3f,sizeof dis); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); sort(e+1,e+m+1); for(int i=1;i<=m;i++) { int u=e[i].u,v=e[i].v,w=e[i].w; u=get(u),v=get(v); if(u==v) { spc[++cnt]=e[i]; continue; } fa[u]=v; add_edge(e[i].u,e[i].v,w); add_edge(e[i].v,e[i].u,w); } dfs1(1,0); dfs2(1,1); for(int i=1;i<=cnt;i++) { int u=spc[i].u,v=spc[i].v,w=spc[i].w; add_edge(u,v,w); add_edge(v,u,w); } for(int i=1;i<=cnt;i++) { int u=spc[i].u,v=spc[i].v; dijkstra(u,dis[(i<<1)-1]); dijkstra(v,dis[i<<1]); } scanf("%d",&q); while(q--) { int a,b;scanf("%d%d",&a,&b); ll ans=d[a]+d[b]-2*d[lca(a,b)]; for(int i=1;i<=cnt;i++) { int u=(i<<1)-1,v=i<<1,w=spc[i].w; ans=min(ans,dis[u][a]+dis[v][b]+w); ans=min(ans,dis[u][b]+dis[v][a]+w); } printf("%lld\n",ans); } return 0; }