[bzoj1880] P2149 [SDOI2009]Elaxia的路线

https://www.luogu.com.cn/problem/P2149
https://darkbzoj.tk/problem/1880
最近bzoj又不能上了,估计以后可能还没有dbzoj活得长

最短路+建出最短路经过的DAG并拓扑排序


\(1\le n\le 1500,1\le w\le 10^4\)


一开始想暴力把每两个点的最短路用dij跑出来,然后枚举两个点判断(为什么可以直接枚举两个点在下面会说),结果发现边数没有要求,边数稍微多点就T,只有55

边权为正,最短路肯定没环,那么如果把所有 \(s\)\(t\) 的最短路中,可能经过(因为最短路可能有多条)的边选出来,建一个新图,那么新图是个DAG
那么如何建这个DAG?分别以 \(s,t\) 为源点,跑两个dij,然后枚举每一条边 \((u,v)\)
\(dis_{i,j}\) 表示以 \(i\) 为源点,\(j\) 的最短路,如果 \(dis_{s,u}+w_{u,v}+dis_{t,v}=dis_{s,t}\),显然就可以选这个边

又有一个结论:它们两个走的最长公共路径,一定是一条链
因为假设公共路径是从一个点分开,再从前方另一个点汇合,那么肯定说明这样走会让其中一方走的最短路更段
但又因为前面还会汇合,就可以让另一方也走这一方的路径(最短路总长度也会更短),所以这个路径还是一条链,矛盾

所以,我们以 \(x1,x2,y1,y2\) 分别跑最短路,然后枚举边
当边 \((u,v)\) 同时满足 \(dis_{x1,u}+w_{u,v}+dis_{y1,v}=dis_{x1,y1}\)\(dis_{x2,u}+w_{u,v}+dis_{y2,v}=dis_{x2,y2}\),就选这个边,建DAG
然后拓扑排序找最长链就行了

但是,这只是其中一种情况,也就是它们“并行”一段路,其实还有一种情况是它们“相遇”,也就是最短路重合,但是方向不同
虽然这不符合题目背景,但是:

毕竟题目描述只是给个背景,认真你就输了

所以还要反着判断建一个DAG,拓扑跑两遍

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#include<vector>
#include<queue>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define N 1505
#define M 2500006
struct graph{
	int fir[N],nex[M],to[M],w[M],tot;
	inline void add(int u,int v,int z){
		to[++tot]=v;w[tot]=z;
		nex[tot]=fir[u];fir[u]=tot;
	}
}G,D;
int x1,x2,y1_,y2,n,m;
int in[N],heap[N],size;
int disx1[N],disy1[N],disx2[N],disy2[N];
int len[N];
int indeg[N],que[N],tail,head;
inline void push(int x,int dis[]){
	heap[++size]=x;
	reg int i=size,fa;
	while(i>1){
		fa=i>>1;
		if(dis[heap[fa]]<=dis[heap[i]]) return;
		std::swap(heap[fa],heap[i]);i=fa;
	}
}
inline int pop(int dis[]){
	int ret=heap[1];heap[1]=heap[size--];
	int i=1,ls,rs;
	while((i<<1)<=size){
		ls=i<<1;rs=ls|1;
		if(rs<=size&&dis[heap[rs]]<=dis[heap[ls]]) ls=rs;
		if(dis[heap[ls]]>=dis[heap[i]]) break;
		std::swap(heap[i],heap[ls]);i=ls;
	}
	return ret;
}
inline void dij(int start,int dis[]){
	std::memset(dis,0x3f,(n+1)*sizeof dis[0]);dis[start]=0;
	push(start,dis);
	while(size){
		reg int u=pop(dis),v;in[u]=0;
		for(reg int i=G.fir[u];i;i=G.nex[i]){
			v=G.to[i];
			if(dis[v]>dis[u]+G.w[i]){
				dis[v]=dis[u]+G.w[i];
				if(!in[v]) push(v,dis),in[v]=1;
			}
		}
	}
}
inline void topo(){
	tail=0;head=-1;
	reg int u,v;
	for(reg int i=1;i<=n;i++)if(!indeg[i]) que[++head]=i;
	while(tail<=head){
		u=que[tail++];
		for(reg int i=D.fir[u];i;i=D.nex[i]){
			v=D.to[i];
			len[v]=std::max(len[v],len[u]+D.w[i]);
			if(!--indeg[v]) que[++head]=v;
		}
	}
}
inline void do_topo(){
	for(reg int i=1;i<=n;i++)for(reg int j=G.fir[i];j;j=G.nex[j])
		if(disx1[i]+G.w[j]+disy1[G.to[j]]==disx1[y1_]&&disx2[i]+G.w[j]+disy2[G.to[j]]==disx2[y2])
			D.add(i,G.to[j],G.w[j]),indeg[G.to[j]]++;
	topo();
	std::memset(D.fir,0,sizeof D.fir);std::memset(D.nex,0,sizeof D.nex);
	D.tot=0;
	std::memset(indeg,0,sizeof indeg);
	for(reg int i=1;i<=n;i++)for(reg int j=G.fir[i];j;j=G.nex[j])
		if(disx1[i]+G.w[j]+disy1[G.to[j]]==disx1[y1_]&&disy2[i]+G.w[j]+disx2[G.to[j]]==disx2[y2])
			D.add(i,G.to[j],G.w[j]),indeg[G.to[j]]++;
	topo();
}
int main(){
	n=read();m=read();
	x1=read();y1_=read();x2=read();y2=read();
	for(reg int u,v,z,i=1;i<=m;i++){
		u=read();v=read();z=read();
		G.add(u,v,z);G.add(v,u,z);
	}
	dij(x1,disx1);dij(y1_,disy1);dij(x2,disx2);dij(y2,disy2);
	do_topo();
	reg int ans=0;
	for(reg int i=1;i<=n;i++) ans=std::max(ans,len[i]);
	std::printf("%d",ans);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/suxxsfe/p/12896329.html