codeforces1473E Minimum Path

题目:

给定一个 n n n个结点, m m m条边的图,边有权值,将一条包含边 e 1 , e 2 , . . . e k e_1,e_2,...e_k e1,e2,...ek的路径的权值定义为 ∑ i = 1 k w e i − max ⁡ i = 1 k w e i + min ⁡ i = 1 k w e i \sum_{i=1}^k w_{e_i}- \max_{i=1}^k w_{e_i}+ \min_{i=1}^k w_{e_i} i=1kweimaxi=1kwei+mini=1kwei,问图中从结点1到其他所有结点的最小路径权值为多少。
( 2 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 , 1 ≤ w i ≤ 1 0 9 ) (2 \le n \le 2 \times 10^5,1 \le m \le 2 \times 10^5,1 \le w_i \le 10^9) (2n2×105,1m2×105,1wi109)

题解:

分层图的套路题。
考虑怎么设计状态,如果将 m i n , m a x min,max min,max设计到状态里,那么显然是有后效性的。我们将状态定义为(结点编号 u u u,是否减了一条边的权值 s u b sub sub,是否加了一条边的权值 a d d add add),然后建立分层图跑最短路,结点 i i i的答案就是 d i s i , 1 , 1 dis_{i,1,1} disi,1,1。为什么这样就是对的?跑最短路就是一个贪心的过程,显然只有减去最大权值的边,加上最小权值的边答案才会是最小的,所以最短路的结果就是题目要求的式子。

复杂度: O ( ( m + n ) l o g m ) O((m+n)logm) O((m+n)logm)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const ll INF=1e18;
const int maxn=2e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,m;
vector<pii>g[maxn];
ll dis[maxn][2][2];
int vis[maxn][2][2];
struct node{
	int no,sub,add;
	ll d;
};
struct cmp{
	bool operator()(node a,node b){
		return a.d>b.d;
	}
};
priority_queue<node,vector<node>,cmp>pq;
void dij(){
	for(int i=1;i<=n;i++){
		for(int j=0;j<2;j++){
			for(int k=0;k<2;k++){
				dis[i][j][k]=INF;
			}
		}
	}
	dis[1][0][0]=0;
	pq.push(node{1,0,0,0});
	while(!pq.empty()){
		node u=pq.top();
		pq.pop();
		if(vis[u.no][u.sub][u.add])continue;
		vis[u.no][u.sub][u.add]=1;
		for(auto v:g[u.no]){
			if(u.sub==0){
				if(u.d<dis[v.fi][1][u.add]){
					dis[v.fi][1][u.add]=u.d;
					pq.push(node{v.fi,1,u.add,dis[v.fi][1][u.add]});
				}
			}
			if(u.add==0){
				if(u.d+2*v.se<dis[v.fi][u.sub][1]){
					dis[v.fi][u.sub][1]=u.d+2*v.se;
					pq.push(node{v.fi,u.sub,1,dis[v.fi][u.sub][1]});
				}
			}
			if(u.add==0&&u.sub==0){
				if(u.d+v.se<dis[v.fi][1][1]){
					dis[v.fi][1][1]=u.d+v.se;
					pq.push(node{v.fi,1,1,dis[v.fi][1][1]});
				}
			}
			if(u.d+v.se<dis[v.fi][u.sub][u.add]){
				dis[v.fi][u.sub][u.add]=u.d+v.se;
				pq.push(node{v.fi,u.sub,u.add,dis[v.fi][u.sub][u.add]});
			}
		}
	}
}
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&m);
	int u,v,w;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&u,&v,&w);
		g[u].pb(mp(v,w));
		g[v].pb(mp(u,w));
	}
	dij();
	for(int i=2;i<=n;i++){
		printf("%lld ",dis[i][1][1]);
	}
	puts("");
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值