[BZOJ1880][Sdoi2009] Elaxia的路线 (最短路,拓扑排序)

这道题很不错,要用最短路,建新图,拓扑排序。


我一开始想的是求一遍floyd,找出两个点 u,v,使得 dis[s][u]+dis[u][v]+dis[v][t]=dis[s][t],另一条路线也满足着一个条件,找到最长的dis[u][v],但超时无疑,我就不会改了。


正解:

对4个点分别求一遍最短路,之后枚举每一条边 u-v,若dis1[u]+w+dis2[v]=dis1[t1],则这条边在最短路上,如果另一条路也满足这一条件,则这是两条最短路的公共路线,就把他加入新图中,最后在新图中用拓扑排序求一下最长路.

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue> 
using namespace std;

inline void getint(int &x){
	x=0; int res=1; char c=getchar();
	while(!(c>='0'&&c<='9')){ if(c=='-') res=-1; c=getchar(); }
	while(c>='0' && c<='9') x=x*10+c-'0', c=getchar();
	x*=res;
} 

typedef long long LL;
const int N=1510, M=N*N;
const LL INF=100000000000000;
int n, m, mi, m2, x1, y1, x2, y2;
int head[N], h2[N], du[N];
LL s1[N], t1[N], s2[N], t2[N], tmp[N];
bool inq[N], vis[N];

queue <int> Q;

struct Edge{
	int to,next,w;
}e[N*N], e2[N*N];

inline void add(int u,int v,int w){
	e[++mi] = (Edge){v,head[u],w};
	head[u] = mi;
}

inline void add2(int u,int v,int w){
	du[v]++;
	e2[++m2] = (Edge){v,h2[u],w};
	h2[u] = m2;
}

inline void spfa(int s,LL *d){
	for(int i=1; i<=n; ++i) d[i]=INF, inq[i]=false;
	while(!Q.empty()) Q.pop();
	Q.push(s); d[s]=0;
	int u, v, p;
	while(!Q.empty()){
		u=Q.front(); Q.pop(); inq[u]=false;
		for(p=head[u]; p; p=e[p].next){
			v = e[p].to;
			if(d[v]>d[u]+e[p].w){
				d[v] = d[u]+e[p].w;
				if(!inq[v]){
					inq[v] = true;
					Q.push(v);
				}
			}
		}
	}
}

inline bool check(int u,int v,int w){
	return (s1[u]+(LL)w+t1[v]==s1[y1] && s2[u]+(LL)w+t2[v]==s2[y2])||(s1[u]+(LL)w+t1[v]==s1[y1] && s2[v]+(LL)w+t2[u]==s2[y2]);
}

inline LL topo(){
	LL ans=0;
	while(!Q.empty()) Q.pop();
	for(int i=1; i<=n; ++i) if(!du[i] && vis[i]) Q.push(i);
	int u, v, p;
	while(!Q.empty()){
		u=Q.front(); Q.pop();
		for(p=h2[u]; p; p=e2[p].next){
			v = e2[p].to; tmp[v] = max(tmp[v], tmp[u]+e2[p].w);
			if(!(--du[v])){
				Q.push(v);
				//tmp[v] = max(tmp[v], tmp[u]+e2[p].w);
			}
		}
	}
	for(int i=1; i<=n; ++i) ans=max(ans,tmp[i]);
	return ans;
}

int main(){
	scanf("%d%d%d%d%d%d",&n,&m,&x1,&y1,&x2,&y2);
	for(int i=1, u, v, w; i<=m; ++i){
		getint(u); getint(v); getint(w);
		add(u,v,w); add(v,u,w);
	}
	spfa(x1,s1); spfa(y1,t1); spfa(x2,s2); spfa(y2,t2);
	for(int u=1, p, v; u<=n; ++u){
		for(p=head[u]; p; p=e[p].next){
			v = e[p].to;
			if(check(u,v,e[p].w)){ vis[u]=vis[v]=true; add2(u,v,e[p].w); }
		}
	}
	printf("%lld\n",topo());
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值