• 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 
    ",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
    ",solve(x,y));
    	}
    }
    
    int main()
    {
        work();
        return 0;
    }
    

      

  • 相关阅读:
    C语言基础学习8:指针数组
    C语言基础学习7:返回指针值的函数
    C语言基础学习6: 指向函数的指针
    C语言基础学习5:字符串与指针
    C语言基础学习4:数组与指针
    C语言基础学习3:指针
    python 入门 之二 列表 元组 字典 字符串 集合的定义及基本操作
    python入门之路 一
    whl格式 和egg格式
    python 连接mongodb ,并将EXCEL文档导入mongodb
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6505620.html
Copyright © 2020-2023  润新知