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

浙公网安备 33010602011771号