bzoj 4398 福慧双修 题解

卡了一晚上啊

首先我们要跑一边整张图的spfa,记录每个点是从哪条边出去的(pre数组)

这里记录的不是前驱边,而是和原点相连的第一个点编号,因为不能走重复边所以才要记录这个,以免刚刚出去又原路返回

那么有一个朴素思路:spfa每一个1点连出去的点

事实上这样是会T的。。。

从这里改进,我们发现上面那种方法走了很多重复的工作,每一次spfa其实是非常类似的

那么我们可以优化一下这个过程:建一张新图跑spfa

这张新图这样建:建立新汇点n+1,设当前边(u,v,w)

1.原点连出的{

pre[v]!=v的话,连1,v,w

否则不连

}

2.连向原点的{

pre[u]!=u 直接用dis[u]+w更新答案

否则连u,n+1,w

}

3.其他的,如果pre[u]==pre[v]从一到v建dis[u]+w的边

否则保留原边

那么新图的最短路就是答案

某个T掉了的

//Copyright(c)2015 liuchenrui
#include<cstdio>
#include<ctime>
#include<iostream>
#include<algorithm>
#include<cstring>
#define o(e) ((((e)-1)^1)+1)
#define inc(a) a++;if(a==100000)a=1;
#define inf 1000000000
using namespace std;
inline void splay(int &v){
	v=0;char c=0;int p=1;
	while(c<'0' || c>'9'){if(c=='-')p=-1;c=getchar();}
	while(c>='0' && c<='9'){v=(v<<3)+(v<<1)+c-'0';c=getchar();}
	v*=p;
}
struct Edge{
	int to,next,len;
}edge[200010];
int first[100010],size;
int dis[40010],dl[100010];
bool exsit[40010];
void addedge(int x,int y,int z){
	size++;
	edge[size].to=y;
	edge[size].next=first[x];
	first[x]=size;
	edge[size].len=z;
}
int head,tail;
int spfa(int now,int lim){
	head=0,tail=1;
	memset(dis,63,sizeof dis);
	dis[now]=0,dl[1]=now;
	while(head!=tail){
		inc(head);int v=dl[head];exsit[v]=false;
		for(int u=first[v];u;u=edge[u].next){
			if(edge[u].len+dis[v]<dis[edge[u].to] && u!=lim){
				dis[edge[u].to]=edge[u].len+dis[v];
				if(!exsit[edge[u].to]){
					exsit[edge[u].to]=true;
					inc(tail);dl[tail]=edge[u].to;
				}
			}
		}
	}
	return dis[1];
}
int main(){
	freopen("xxx.in","r",stdin);
	freopen("xxx.out","w",stdout);
	int n,m;splay(n),splay(m);
	for(int i=1;i<=m;i++){
		int s,e,l,r;
		splay(s),splay(e),splay(l),splay(r);
		addedge(s,e,l);addedge(e,s,r);
	}
	int ans=inf;
	for(int u=first[1];u;u=edge[u].next){
		ans=min(ans,spfa(edge[u].to,o(u))+edge[u].len);
	}
	cout<<ans<<endl;
}
A了的

//Copyright(c)2015 liuchenrui
#include<cstdio>
#include<ctime>
#include<iostream>
#include<algorithm>
#include<cstring>
#define o(e) ((((e)-1)^1)+1)
#define inc(a) a++;if(a==100000)a=1;
#define inf 1000000000
using namespace std;
inline void splay(int &v){
	v=0;char c=0;int p=1;
	while(c<'0' || c>'9'){if(c=='-')p=-1;c=getchar();}
	while(c>='0' && c<='9'){v=(v<<3)+(v<<1)+c-'0';c=getchar();}
	v*=p;
}
struct Edge{
	int to,next,len,from;
}edge[300010],e[300010];
int first[100010],size;
int dis[40010],dl[100010];
bool exsit[40010];
int pre[40010];
int f[40010],s;
int n,m;
void addedge(int x,int y,int z){
	size++;
	edge[size].to=y;
	edge[size].next=first[x];
	first[x]=size;
	edge[size].len=z;
	edge[size].from=x;
}
void add(int x,int y,int z){
	s++;
	e[s].to=y;
	e[s].next=f[x];
	f[x]=s;
	e[s].len=z;
	fprintf(stderr,"%d %d %d\n",x,y,z);
}
int head,tail,ans=inf;
void spfa(){
	while(head!=tail){
		inc(head);int v=dl[head];exsit[v]=false;
		for(int u=first[v];u;u=edge[u].next){
			if(edge[u].len+dis[v]<dis[edge[u].to]){
				dis[edge[u].to]=edge[u].len+dis[v];
				pre[edge[u].to]=pre[v];
				if(!exsit[edge[u].to]){
					exsit[edge[u].to]=true;
					inc(tail);dl[tail]=edge[u].to;
				}
			}
		}
	}
}
int Spfa(){
	memset(dis,63,sizeof dis);
	head=0,tail=1;dis[1]=0;dl[1]=1;
	while(head!=tail){
		inc(head);int v=dl[head];exsit[v]=false;
		for(int u=f[v];u;u=e[u].next){
			if(e[u].len+dis[v]<dis[e[u].to]){
				dis[e[u].to]=e[u].len+dis[v];
				if(!exsit[e[u].to]){
					exsit[e[u].to]=true;
					inc(tail);dl[tail]=e[u].to;
				}
			}
		}
	}
	return dis[n+1];
}
int main(){
	freopen("xxx.in","r",stdin);
	freopen("xxx.out","w",stdout);
	splay(n),splay(m);
	for(int i=1;i<=m;i++){
		int s,e,l,r;
		splay(s),splay(e),splay(l),splay(r);
		if(s==e&&s==1){
			ans=min(ans,l),ans=min(ans,r);
		}
		else addedge(s,e,l),addedge(e,s,r);
	}
	memset(dis,63,sizeof dis);
	for(int u=first[1];u;u=edge[u].next){
		dl[++tail]=edge[u].to;
		exsit[edge[u].to]=true;
		pre[edge[u].to]=edge[u].to;
		dis[edge[u].to]=edge[u].len;
	}
	spfa();
	for(int i=1;i<=size;i++){
		if(edge[i].to==1){
			if(edge[i].from==pre[edge[i].from]){
				add(edge[i].from,n+1,edge[i].len);
			}
			else{
				ans=min(ans,dis[edge[i].from]+edge[i].len);
			}
		}
		else if(edge[i].from==1){
			if(pre[edge[i].to]!=edge[i].to){
				add(1,edge[i].to,edge[i].len);
			}
		}
		else{
			if(pre[edge[i].from]==pre[edge[i].to]){
				add(edge[i].from,edge[i].to,edge[i].len);
			}
			else{
				add(1,edge[i].to,dis[edge[i].from]+edge[i].len);
			}
		}
		
	}
	cout<<Spfa()<<endl;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值