采蘑菇
题目描述
小胖和 ZYR 要去 ESQMS 森林采蘑菇。
ESQMS 森林间有 \(N\) 个小树丛,\(M\) 条小径,每条小径都是单向的,连接两个小树丛,上面都有一定数量的蘑菇。小胖和 ZYR 经过某条小径一次,可以采走这条路上所有的蘑菇。由于 ESQMS 森林是一片神奇的沃土,所以一条路上的蘑菇被采过后,又会长出一些新的蘑菇,数量为原来蘑菇的数量乘上这条路的“恢复系数”,再下取整。
比如,一条路上有 \(4\) 个蘑菇,这条路的“恢复系数”为 \(0.7\),则第一~四次经过这条路径所能采到的蘑菇数量分别为 \(4,2,1,0\)。
现在,小胖和 ZYR 从 \(S\) 号小树丛出发,求他们最多能采到多少蘑菇。
思路
由于每一条边都是可以重复经过得,所以考虑找到一个环使得将环内所有得蘑菇都采集完后,再进入下一个环,这样得采集方式可以使得最后得答案最大。那么这些环上得点可以缩成一个点,让这个环上所有得贡献都变成缩点后得点权。将所有得联通分量缩点之后,就是一个有向无环得图,这样就可以考虑从起点 \(S\) 出发求得到达每一个点得最大贡献是多少从而确定最后得答案,也就是在 \(DAG\) 上用 \(spfa\) 跑一遍最长路。
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
std::cin >> n >> m;
std::vector<std::array<int, 3>> G[n + 1];
for (int i = 1; i <= m; i++) {
int u, v, w;
double k;
std::cin >> u >> v >> w >> k;
G[u].push_back({v, w, k * 10});
}
std::vector<int> dfn(n + 1), stk(n + 1), low(n + 1);
std::vector<int> scc(n + 1);
std::vector<bool> vis(n + 1);
int idx = 0, top = 0, cnt = 0;
// idx是时间戳,top是栈的指针,cnt是强连通分量的数量->缩点的个数
std::function<void(int)> tarjan = [&] (int u) -> void {
dfn[u] = low[u] = ++ idx;
stk[++top] = u;
vis[u] = true;
for (auto& [v, w, k] : G[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = std::min(low[u], low[v]);
} else if (vis[v]) {
low[u] = std::min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {//时间戳和回溯点是同一个那就是缩点
int v = 0;
cnt++;
do {
v = stk[top--];
vis[v] = false;
scc[v] = cnt;
} while(v != u);
}
};
for (int i = 1; i <= n; i++) {
if (!dfn[i]) tarjan(i);
}
std::vector<std::array<int, 2>> adj[cnt + 1];
std::vector<int> sum(cnt + 1);
for (int i = 1; i <= n; i++) {
for (auto& [v, w, k] : G[i]) {
if (scc[i] == scc[v]) {
while(w) sum[scc[i]] += w, w = w * k / 10;
} else {
adj[scc[i]].push_back({scc[v], w});
}
}
}
int s; std::cin >> s;
s = scc[s];
std::queue<int> q;
q.push(s);
std::vector<int> dist(cnt + 1, -1);
std::vector<bool> st(cnt + 1);
st[s] = true;
dist[s] = sum[s];
while(!q.empty()) {
int u = q.front();
q.pop();
st[u] = false;
for (auto& [v, w] : adj[u]) {
if (dist[v] < dist[u] + w + sum[v]) {
dist[v] = dist[u] + w + sum[v];
if (!st[v]) {
st[v] = true;
q.push(v);
}
}
}
}
std::cout << *max_element(all(dist)) << "\n";
return 0 ^ 0;
标签:std,Tarjan,int,Luogu,cnt,vector,low,蘑菇,P2656
From: https://www.cnblogs.com/Haven-/p/16813649.html