• YbtOJ#631次短路径【左偏树,最短路】


    正题

    题目链接:https://www.ybtoj.com.cn/contest/114/problem/1


    题目大意

    给出\(n\)个点\(m\)条边的一张无向图,对于每个点\(i\)求不经过\(i\sim 1\)的最短路的第一条边的情况下\(i\)\(1\)的最短路

    数据保证这条边唯一

    \(n\in[1,10^5],m\in[1,2\times 10^5],c\in[1,10^3]\)


    解题思路

    因为保证的那个东西,所以图的最短路树真的是一棵树了,所以先跑出最短路树考虑在最短路树上面搞。

    然后题目限制了我们不能从树上的祖先那条边过来,这样就分为了两种情况。一种是从该点的子树外面连过来的边,另一种是从子树中走上来的边。第二种很麻烦,因为子树的最短路是用该节点的最短路扩展的,所以不能直接使用。

    考虑一条非树边\((x,y)\),这条边会扩展一条\(dis_y+w\)\(x\)的路径。(\(dis_x\)表示\(1\sim x\)的最短路)。

    并且这条边可以使用到\(LCA(x,y)\)处,此时\(x\)的祖先们都不包含\(y\)在子树内,可以直接用\(y\)的子树扩展。

    所以可以维护一个左偏树,每次合并两个儿子的信息,如果堆顶的边需要被删除就删除。需要写一个\(lazy\)标记来修改整棵树

    时间复杂度\(O(n\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #define mp(x,y) make_pair(x,y)
    using namespace std;
    const int N=4e5+10;
    struct point{
    	int val,x,y;
    	point(int v=0,int xx=0,int yy=0)
    	{val=v;x=xx;y=yy;return;}
    };
    bool operator<(point x,point y)
    {return x.val<y.val;}
    struct Heap{
    	point val[N];
    	int t[N][2],lazy[N],dis[N];
    	void Downdata(int x){
    		if(!lazy[x])return;
    		int ls=t[x][0],rs=t[x][1];
    		lazy[ls]+=lazy[x];lazy[rs]+=lazy[x];
    		val[ls].val+=lazy[x];val[rs].val+=lazy[x];
    		lazy[x]=0;return;
    	}
    	int Merge(int x,int y){
    		Downdata(x);Downdata(y);
    		if(!x||!y)return x+y;
    		if(val[y]<val[x])swap(x,y);
    		int &ls=t[x][0],&rs=t[x][1];
    		rs=Merge(rs,y);
    		if(dis[rs]>dis[ls])swap(ls,rs);
    		dis[x]=dis[rs]+1;return x;
    	}
    	int Del(int x){
    		int &ls=t[x][0],&rs=t[x][1];val[x]=0;
    		return Merge(ls,rs);
    	}
    }T;
    struct node{
    	int to,next,w;
    }a[N];
    int n,m,tot,cnt,num,ls[N],f[N];
    int rfn[N],p[N],ans[N];
    bool v[N];vector<int> G[N];
    priority_queue<pair<int,int> >q;
    void addl(int x,int y,int w){
    	a[++tot].to=y;
    	a[tot].next=ls[x];
    	ls[x]=tot;a[tot].w=w;
    	return;
    }
    void dij(){
    	memset(f,0x3f,sizeof(f));
    	q.push(mp(0,1));f[1]=0;
    	while(!q.empty()){
    		int x=q.top().second;q.pop();
    		if(v[x])continue;v[x]=1;
    		for(int i=ls[x];i;i=a[i].next){
    			int y=a[i].to;
    			if(f[x]+a[i].w<f[y]){
    				f[y]=f[x]+a[i].w;
    				q.push(mp(-f[y],y));
    			}
    		}
    	}
    	return;
    }
    void dfs(int x){
    	rfn[x]=++cnt;
    	for(int i=0;i<G[x].size();i++){
    		int y=G[x][i];dfs(y);
    		T.val[p[y]].val+=f[y]-f[x];
    		T.lazy[p[y]]+=f[y]-f[x];
    		p[x]=T.Merge(p[x],p[y]);
    	}
    	for(int i=ls[x];i;i=a[i].next){
    		int y=a[i].to;
    		if(f[x]+a[i].w==f[y])continue;
    		if(f[y]+a[i].w==f[x])continue;
    		T.val[++num]=point(f[y]+a[i].w,x,y);
    		p[x]=T.Merge(p[x],num);
    	}
    	while(1){
    		if(!p[x]){ans[x]=-1;break;}
    		point w=T.val[p[x]];
    		if(rfn[w.y]>=rfn[x])
    		{p[x]=T.Del(p[x]);continue;}
    		ans[x]=w.val;break;
    	}
    	return;
    }
    int main()
    {
    	freopen("pal.in","r",stdin);
    	freopen("pal.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		int x,y,w;
    		scanf("%d%d%d",&x,&y,&w);
    		addl(x,y,w);addl(y,x,w);
    	}
    	dij();
    	for(int x=1;x<=n;x++)
    		for(int i=ls[x];i;i=a[i].next){
    			int y=a[i].to;
    			if(f[x]+a[i].w==f[y])
    				G[x].push_back(y);
    		}
    	dfs(1);
    	for(int i=2;i<=n;i++)
    		if(!ans[i])puts("-1");
    		else printf("%d\n",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    POJ 1320 Street Numbers 解佩尔方程
    数学分支(转)
    深入理解Java类加载器(1):Java类加载原理解析
    Java类加载器的工作原理
    深入理解Java:类加载机制及反射
    类加载机制:全盘负责和双亲委托
    java底层学习
    代码面试最常用的10大算法
    程序员面试金典算法题
    了解ASCII、gb系列、Unicode、UTF-8的区别
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14403830.html
Copyright © 2020-2023  润新知