【杭电多校第四场 B题】最短路图+缩点dp

题目链接

题目描述:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题意:

题意就是给你一个有向图,每条边有两个权值,问第一个权值的最短路是多少,而且在第一个权值最小的情况下第二个权值之和的最大值是多少

分析:

很容易想到的就是跑一个最短路然后建一个最短路图再跑一个最长路,因为可能有环,所以跑一个spfa就直接出来,好多人都是这么想的,但是这样想是不对的,因为有环的话跑最长路一定是有问题的,所以说咱们可以用tarjan算法把这个有向图给缩成DAG,然后dp就行,dp[i]代表从1号点所在的连通块到i号联通块的第二个权值之和的最大值,下面请看代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200010,M = 300010;
int h[N],e[M+M],w[M+M],p[M+M],ne[M+M],idx;
int h1[N],e1[M+M],w1[M+M],p1[M+M],ne1[M+M],idx1;
inline void add(int a,int b,int c,int d) {
	e[idx] = b;
	w[idx] = c;
	p[idx] = d;
	ne[idx] = h[a];
	h[a] = idx++;
}
inline void add1(int a,int b,int c,int d) {
	e1[idx1] = b;
	w1[idx1] = c;
	p1[idx1] = d;
	ne1[idx1] = h1[a];
	h1[a] = idx1++;
}
inline int read() {
	int f=1,x=0;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return f*x;
}
int n,m;
int dfn[N],low[N],bel[N],tt,cnt,ww[N];
bool ins[N];
stack<int> stk;
vector<vector<int> > scc;
bool st[N];
int dis[N];
int dijkstra(int start,int end) {
	for(int i=1; i<=n; i++) dis[i] = 0x3f3f3f3f3f3f3f3f,st[i] = false;
	dis[start] = 0;
	priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
	q.push(make_pair(0,start));
	while(q.size()) {
		int t = q.top().second;
		q.pop();
		if(st[t]) continue;
		st[t] = true;
		for(int i=h[t]; i!=-1; i=ne[i]) {
			int j = e[i];
			if(dis[j] > dis[t] + w[i]) {
				dis[j] = dis[t] + w[i];
				q.push(make_pair(dis[j],j));
			}
		}
	}
}
void dfs(int u) {
	dfn[u] = low[u] = ++tt;
	stk.push(u);
	ins[u] = true;
	for(int i=h1[u]; i!=-1; i=ne1[i]) {
		int v = e1[i];
		if(!dfn[v]) dfs(v);
		if(ins[v]) low[u] = min(low[u],low[v]);
	}
	if(low[u] == dfn[u]) {
		++cnt;
		while(true) {
			int v = stk.top();
			stk.pop();
			ins[v] = false;
			bel[v] = cnt;
			if(u == v) break;
		}
	}
}
int deg[N],f[N];
void topo(){
	queue<int> q;
	for(int i=1;i<=cnt;i++) if(!deg[i]) q.push(i);
	while(!q.empty()){
		int t = q.front();
		q.pop();
		for(int i=h[t];i!=-1;i=ne[i]){
			int j = e[i];
			deg[j]--;
			f[j] = max(f[j],f[t]+p[i]);
			if(!deg[j]) q.push(j);
		}
	}
}
void solve() {
	n = read(),m = read();
	idx1 = cnt = idx = tt = 0;
	for(int i=1; i<=n; i++) h[i] = h1[i] = -1,ins[i] = f[i] = ww[i] = dfn[i] = low[i] = bel[i] = deg[i] = 0;
	while(!stk.empty()) stk.pop();
	while(m--) {
		int u = read(),v = read(),e = read(),p = read();
		add(u,v,e,p);
	}
	dijkstra(1,n);
	printf("%lld ",dis[n]);
	for(int i=1;i<=n;i++){
		for(int k=h[i];k!=-1;k=ne[k]){
			int j = e[k];
			if(dis[j] == dis[i] + w[k]) add1(i,j,w[k],p[k]);
		}
	}
	for(int i=1;i<=n;i++) h[i] = -1;idx = 0;
	for(int i=1; i<=n; i++) if(!dfn[i]) dfs(i);
	for(int i=1; i<=n; i++) {
		for(int k=h1[i]; k!=-1; k=ne1[k]) {
			int j = e1[k];
			if(bel[i] != bel[j]) add(bel[i],bel[j],w1[k],p1[k]),deg[bel[j]]++;
		}
	}
	topo();
	printf("%lld\n",f[bel[n]]);
}
signed main() {
	int _=read();
	while(_--) solve();
	return 0;
}
/*
2
3 3
1 2 1 1
2 3 1 1
1 3 2 0
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇智波一打七~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值