题解 P2656 【采蘑菇】

Ireliaღ

2019-07-19 09:41:31

Solution

**Tarjan缩点+拆点+Spfa最长路** ## 解题思路 - 缩点。显然,进入一个强连通分量,可以把里面的边全部**走满** - 拆点,分为入点和出点 - 建图 - 对于不同强连通分量的边,正常建,边权为**走一次**的边权,注意是入点连出点 - 对于相同强连通分量的边,把**走满**的边权加到连接这个点入点和出点的边权上 - 从起点的入点`Spfa`最长路,就可以不用考虑点权了。然后在所有出点中找距离最长的。 ## 代码 ```cpp #include <iostream> #include <cstring> #include <stack> #include <queue> #include <cmath> using namespace std; const int MAXN = 8e4 + 5; const int MAXM = 2e5 + 5; struct Edge{ int to, val; Edge *nxt; Edge(int to, int val, Edge *nxt) : to(to), val(val), nxt(nxt) {} }; Edge *hd1[MAXN], *hd2[MAXN]; void Add1(int u, int v, int w) { hd1[u] = new Edge(v, w, hd1[u]); } void Add2(int u, int v, int w) { hd2[u] = new Edge(v, w, hd2[u]); } int n, m; int inx[MAXM], iny[MAXM], inz[MAXM]; double inr[MAXM]; int low[MAXN], col[MAXN], dfn[MAXN], tc, tot; stack<int> stk; int w[MAXN << 1], dis[MAXN << 1]; bool vis[MAXN << 1]; void Tarjan(int u) { stk.push(u); dfn[u] = low[u] = ++tc; for (Edge *e = hd1[u]; e; e = e->nxt) { int v = e->to; if (!dfn[v]) { Tarjan(v); low[u] = min(low[u], low[v]); } else { if (!col[v]) low[u] = min(low[u], dfn[v]); } } if (low[u] == dfn[u]) { col[u] = ++tot; while (!stk.empty()) { int v = stk.top(); stk.pop(); col[v] = tot; if (v == u) break; } } } int GetTotal(int x, double y) { int ret = 0; while (x) { ret += x; x = (int)floor((double)x * y); } return ret; } int Id(int u, int typ) { if (typ == 0) return col[u]; else return col[u] + tot; } void Rebuild() { for (int i = 1; i <= m; i++) { if (col[inx[i]] == col[iny[i]]) w[col[inx[i]]] += GetTotal(inz[i], inr[i]); else Add2(Id(inx[i], 1), Id(iny[i], 0), inz[i]); } for (int i = 1; i <= tot; i++) Add2(i, i + tot, w[i]); } void Spfa(int s) { memset(dis, 0x8f, sizeof(dis)); queue<int> q; q.push(s); dis[s] = 0; vis[s] = true; while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = false; for (Edge *e = hd2[u]; e; e = e->nxt) { int v = e->to; if (dis[v] < dis[u] + e->val) { dis[v] = dis[u] + e->val; if (!vis[v]) { vis[v] = true; q.push(v); } } } } } int main() { cin >> n >> m; for (int i = 1; i <= m; i++) { cin >> inx[i] >> iny[i] >> inz[i] >> inr[i]; Add1(inx[i], iny[i], inz[i]); } for (int i = 1; i <= n; i++) { if (!dfn[i]) Tarjan(i); } for (int i = 1; i <= n; i++) { cerr << col[i] << ' '; } cerr << endl; Rebuild(); int s; cin >> s; Spfa(Id(s, 0)); int ans = 0; for (int i = 1; i <= tot * 2; i++) ans = max(ans, dis[i]); cout << ans << endl; return 0; } ```