BZOJ2125 最短路
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
题目链接:BZOJ2125
正解:仙人掌+圆方树
解题报告:
原来圆方树就是我以前写过的tarjan的点双缩点…为啥搞得这么高级…
原树上的点被称为圆点,对于每个环建立一个新的点,被称为方点,方点向环内的所有点连边,边权为到环的顶部的距离。
那么我们发现这样建图之后,仙人掌变成了一棵树(圆方树),而且如果不考虑环上的情况,直接倍增LCA求出的距离是等价于原图的。
下面我们考虑假设两个点的LCA是一个方点,那么LCA就不能直接算距离了。
我们留下最后一步不往上跳,也就是会落在两个圆点上,显然这两个点原来处在一个环上,那么我先计算完x、y到这两个点的距离,再加上这两个点在环上的最短路即可。
这种题目一般都是环上特判,然后其余的建出圆方树之后就可以直接跟树上一样做了。
ps:我学的可能是假的圆方树...
问了一下cjl大爷,发现窝的两个地方都处理萎了...(然而还是过了数据!)
首先我记录每个点属于哪个环中时,因为我的疏忽,会记录处理的最后一个环...
所以离连边就很容易萎了,然而我拍了几万组没拍WA没拍RE是smg...
cjl大爷告诉我的处理方法就是:当且仅当,一个点作为环的儿子(也就是不是环的顶部时)才记录belong,如果是环的顶部就不记录了,考虑一个点只能是一个方点的儿子、多个方点的父亲,这样做显然没有问题...
而且连边的话也没有我写的那么复杂,在tarjan的时候就可以顺便连好了。
当且仅当low小于等于dfn才连,似乎这样可以避开很多讨论了...就不用额外处理连边了。
//It is made by ljh2000 #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <ctime> #include <vector> #include <queue> #include <map> #include <set> #include <string> #include <complex> using namespace std; typedef long long LL; const int MAXN = 50011; const int MAXM = 100011; int n,m,Q,N; int cnt,belong[MAXN],len[MAXN],sum[MAXN],son[MAXN]; vector<int>C[MAXN]; inline int getint(){ int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } namespace YFTree{ int n,ecnt,first[MAXN],to[MAXM],next[MAXM],w[MAXM]; int deep[MAXN],f[MAXN][16],g[MAXN][16],ans; inline void link(int x,int y,int z){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z; next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z; //printf("link : %d and %d val=%d \n",x,y,z); } inline void dfs(int x,int fa){ for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; f[v][0]=x; g[v][0]=w[i]; deep[v]=deep[x]+1; dfs(v,x); } } inline void prepare(){ for(int j=1;j<=15;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1],g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1]; } inline void lca(int &x,int &y){ if(deep[x]<deep[y]) swap(x,y); int t=0; while((1<<t)<=deep[x]) t++; t--; for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) ans+=g[x][i],x=f[x][i]; if(x==y) return ; for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) ans+=g[x][i],ans+=g[y][i],x=f[x][i],y=f[y][i]; return ; } inline int solve(int x,int y){ int xx=x,yy=y; ans=0; lca(xx,yy); if(xx==yy) return ans; if(f[xx][0]<=N) { ans+=g[xx][0]; ans+=g[yy][0]; return ans; } int now,chang=len[ belong[xx] ]; if(sum[xx]>sum[yy]) swap(xx,yy); now=min(sum[yy]-sum[xx],chang-(sum[yy]-sum[xx])); ans+=now; return ans; } } int ecnt,first[MAXN],to[MAXM],next[MAXM],w[MAXM],quan[MAXN]; int dfn[MAXN],low[MAXN],father[MAXN],dis[MAXN]; inline void link(int x,int y,int z){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z; } inline void build(int rt,int x,int ll){ YFTree::n++; int bel=YFTree::n; cnt++; len[cnt]=dis[x]-dis[rt]+ll; belong[x]=cnt; C[cnt].push_back(x); for(int i=x;i!=rt;i=father[i]) belong[father[i]]=cnt,son[father[i]]=i,C[cnt].push_back(father[i]); sort(C[cnt].begin(),C[cnt].end()); sum[rt]=0; for(int i=rt;i!=x;i=son[i]) sum[son[i]]=sum[i]+quan[son[i]]; YFTree::link(bel,rt,0);//!!! for(int i=rt;i!=x;i=son[i]) YFTree::link(bel,son[i],min(sum[son[i]],len[cnt]-sum[son[i]])); } inline void dfs(int x,int fa){ dfn[x]=low[x]=++ecnt; for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; if(!dfn[v]) { father[v]=x; quan[v]=w[i]; dis[v]=dis[x]+w[i]; dfs(v,x); low[x]=min(low[x],low[v]); } else { low[x]=min(low[x],dfn[v]); if(low[v]==dfn[x])//环 build(x,v,w[i]); } } } inline bool find(int cir,int x){ int l=0,r=C[cir].size()-1,mid; while(l<=r) { mid=(l+r)>>1; if(C[cir][mid]==x) return true; if(C[cir][mid]<x) l=mid+1; else r=mid-1; } return false; } inline void work(){ n=YFTree::n=N=getint(); m=getint(); Q=getint(); int x,y,z; for(int i=1;i<=m;i++) { x=getint(); y=getint(); z=getint(); link(x,y,z); link(y,x,z); } ecnt=0; dfs(1,0); for(int i=1;i<=n;i++) {//把没连的边连上! if(father[i]==0) continue; //if(belong[ father[i] ]!=0 && belong[i]!=0) continue;连错辣!!! if(belong[father[i]]==0 || belong[i]==0) YFTree::link(father[i],i,quan[i]); else if(!find(belong[father[i]],i) && !find(belong[i],father[i])) YFTree::link(father[i],i,quan[i]); } using namespace YFTree; YFTree::dfs(1,0); prepare(); while(Q--) { x=getint(); y=getint(); printf("%d\n",solve(x,y)); } } int main() { work(); return 0; }