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

  

posted @ 2017-03-05 15:32  ljh_2000  阅读(210)  评论(0编辑  收藏  举报