2022杭电多校第四场 Link with Running
Problem - 7175 Link with Running
题意:给定 \(n\) 个点 \(m\) 条边的有向图,走每条边需要花费 \(e_i\) 的能量且得到 \(p_i\) 的分数,问从1号点开始走到n号点,当花费最少时得分最多,输出花费和得分,保证答案存在。
知识点:缩点,最短路
首先的想法就是dijkstra的过程中同时计算最短路和得分,但是由于0费用的存在,答案是跑不对的,例如下面这个样例
3 3
1 2 0 1
1 3 0 2
2 3 0 3
如果你的dijkstra不是加了vis数组判重的,那你会跑得过去,否则是错的,但是我们需要加这个vis判重,就是因为有0环的存在,如果不加就T了
问题就是出在0环的地方,我们考虑将0环处理掉
值得注意的是,我们用到的边都是最短路边,所以我们可以先跑一遍最短路处理出最短路图
然后在0环肯定出现在最短路图上,我们直接tarjan缩点,使其变成DAG,然后在DAG上DP即可。
#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, int> PLI;
const int INF = 0x3f3f3f3f, N = 1e5 + 10;
const int MOD = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}
struct node {
int nex;
int cost, val;
};
int dfn[N], low[N], timeStamp;
int scc_cnt, sz[N], idx[N];
int stk[N], tt, n;
int deg[N];
bool in_stk[N];
LL dp[N];
LL dis[N];
vector<node> g1[N], g2[N], g3[N];
void init() {
timeStamp = scc_cnt = tt = 0;
for (int i = 1; i <= n; i ++ ) {
g1[i].clear(), g2[i].clear(), g3[i].clear();
in_stk[i] = false;
dp[i] = deg[i] = 0;
dfn[i] = low[i] = 0;
sz[i] = idx[i] = 0;
}
}
void dijkstra() {
for (int i = 1; i <= n; i ++ ) dis[i] = 1e18;
dis[1] = 0;
priority_queue<PLI, vector<PLI>, greater<PLI>> q;
q.push({dis[1], 1});
vector<bool> vis(n + 1);
while (!q.empty()) {
auto t = q.top(); q.pop();
int u = t.second;
if (vis[u]) continue;
vis[u] = true;
for (auto ite : g1[u]) {
int v = ite.nex, w = ite.cost;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
q.push({dis[v], v});
}
}
}
}
void tarjan(int u) {
dfn[u] = low[u] = ++timeStamp;
stk[++ tt] = u, in_stk[u] = true;
for (auto ite : g2[u]) {
int v = ite.nex;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (in_stk[v]) low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
int y;
scc_cnt ++;
do {
y = stk[tt --];
sz[scc_cnt] ++;
idx[y] = scc_cnt;
in_stk[y] = false;
} while (y != u);
}
}
void topSort() {
queue<int> q;
for (int i = 1; i <= scc_cnt; i ++ ) if (deg[i] == 0) q.push(i);
while (!q.empty()) {
int u = q.front(); q.pop();
for (auto ite : g3[u]) {
int v = ite.nex, w = ite.val;
if (dp[v] < dp[u] + w) dp[v] = dp[u] + w;
deg[v] --;
if (deg[v] == 0) q.push(v);
}
}
}
inline void solve() {
int m; cin >> n >> m;
init();
while (m -- ) {
int u, v, w, p;
cin >> u >> v >> w >> p;
g1[u].push_back({v, w, p});
}
dijkstra();
for (int i = 1; i <= n; i ++ ) {
for (auto ite : g1[i]) {
if (dis[ite.nex] == dis[i] + ite.cost) {
g2[i].push_back(ite);
}
}
}
for (int i = 1; i <= n; i ++ ) if (!dfn[i]) tarjan(i);
set<PII> s;
for (int i = 1; i <= n; i ++ ) {
for (auto ite : g2[i]) {
int v1 = idx[i], v2 = idx[ite.nex];
if (v1 == v2) continue;
deg[v2] ++;
g3[v1].push_back({v2, ite.cost, ite.val});
}
}
topSort();
cout << dis[n] << ' ' << dp[idx[n]] << endl;
}
signed main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
auto now = clock();
#endif
ios::sync_with_stdio(false), cin.tie(nullptr);
cout << fixed << setprecision(2);
int T; cin >> T;
while (T -- )
solve();
#ifdef DEBUG
cout << "============================" << endl;
cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
return 0;
}