「BZOJ3694」「FJ2014集训」最短路

「BZOJ3694」「FJ2014集训」最短路

首先树剖没得说了,这里说一下并查集的做法,

对于一条非树边,它会影响的点就只有u(i),v(i)到lca,对于lca-v的路径上所有点x,都可通过1-t-u-v-x,长度为dep[u]+dep[v]+w(i)-dep[x],lca-u同理,

将非树边按dep[u]+dep[v]+w(i)从小到大排序,显然每个点被前一条能更新他的边更新后即是最优解,此时将它与父亲节点合并,修改的时候用并查集向上修改即可。

 

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define MP(a,b) make_pair(a,b)
#define MAXN 1000000
using namespace std;
int fa[MAXN],f[MAXN][20],dep[MAXN];
struct edge
{
	int u,v,w,is,nxt;
	#define u(x)  ed[x].u
	#define v(x)  ed[x].v
	#define w(x)  ed[x].w
	#define is(x) ed[x].is
	#define n(x)  ed[x].nxt
	friend bool operator < (edge a,edge b)
	{
		return a.is==b.is?(dep[a.u]+dep[a.v]+a.w)<(dep[b.u]+dep[b.v]+b.w):a.is<b.is;
	}
}ed[MAXN];
int first[MAXN],num_e;
#define f(x) first[x]
int n,m,n0;
inline void add_e(int u,int v,int w,int t);
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void dfs(int x,int ff,int deep)
{
	f[x][0]=ff;dep[x]=deep;
	for(int i=f(x);i;i=n(i))
	if(v(i)!=ff && is(i))
		dfs(v(i),x,deep+w(i));
}
int dis[MAXN];
signed main()
{
//	freopen("in.txt","r",stdin);

	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)fa[i]=i;
	int ai,bi,wi,ti;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&ai,&bi,&wi,&ti);
		add_e(ai,bi,wi,ti),add_e(bi,ai,wi,ti);
		if(!ti)n0+=2;
	}
	dfs(1,0,0);
	for(int i=1;i<20;i++)
		for(int j=1;j<=n;j++)
		f[j][i]=f[f[j][i-1]][i-1];
	sort(ed+1,ed+m*2+1);dis[1]=0;
	for(int i=1;i<=n0;i++)
	{
		int x=u(i),y=v(i);
		while(x!=y)
		{
			if(dep[x]<dep[y])swap(x,y);
			if(!dis[x])dis[x]=dep[u(i)]+dep[v(i)]+w(i)-dep[x];
			fa[x]=f[x][0];
			x=fa[x]=find(fa[x]);
		}
	}
	for(int i=2;i<=n;i++)
		printf("%d ",!dis[i]?-1:dis[i]);
}
inline void add_e(int u,int v,int w,int t)
{
	++num_e;
	u(num_e)=u;
	v(num_e)=v;
	w(num_e)=w;
	is(num_e)=t;
	n(num_e)=f(u);
	f(u)=num_e;
}

 

 

 

猜你喜欢

转载自www.cnblogs.com/Al-Ca/p/11182972.html