tarjan习题讲解

CF894E

题目描述

\(n\) 个点, \(m\) 条边, 每次经过一条路径, 路径上蘑菇减少, 求出从 \(s\) 开始捡到的最多蘑菇。

思路

可以先缩点, 求出一个大点内的最大蘑菇, 然后做 \(DAG\) 上的 DP

推式子, 可以发现, 蘑菇数为 \(x\) 的最大贡献为 : 记 \(m \le 0\)\((1 + m) \cdot m / 2 <= x\)\((1 + m + 1) \cdot (m + 1) > x\)

贡献为 \((m + 1) \cdot x - ((m + 1) \cdot (2m + 1) \cdot m / 6 + m(m + 1) / 2)\)

时空均为 \(O(n + m)\)

代码

#include<bits/stdc++.h>

using namespace std;

using LL = long long;

const int N = 1e6 + 5;

struct Node{
  int a, b;
};

vector<Node>g[N], e[N];

long long dp[N], w[N], ans;

int l, r, mid, dfn[N], cnt, tot, top, low[N], stk[N], n, m, u, v, ww, s, color[N], in[N];

LL cost(int x){
  l = 0, r = 20000;
  while(l < r){
    mid = (l + r + 1) >> 1;
    if(1ll * mid * (mid + 1) / 2 <= x){
      l = mid;
    }
    else{
      r = mid - 1;
    }
  }
  return 1ll * (l + 1) * x - (1ll * (l + 1) * (l * 2 + 1) * l / 6 + 1ll * l * (l + 1) / 2) / 2;
}

void tarjan(int x){
  dfn[x] = low[x] = ++cnt, stk[++top] = x;
  for(auto [v, w] : g[x]){
    if(!dfn[v]){
      tarjan(v);
      low[x] = min(low[x], low[v]);
    }
    else if(!color[v]){
      low[x] = min(low[x], dfn[v]);
    }
  }
  if(low[x] == dfn[x]){
    ++tot;
    for(; dfn[stk[top]] >= dfn[x]; color[stk[top]] = tot, top--){
    }
  }
}

int main(){
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  for(int i = 1; i <= m; ++i){
    cin >> u >> v >> ww;
    g[u].push_back({v, ww});
  }
  for(int i = 1; i <= n; ++i){
    if(!dfn[i]){
      tarjan(i);
    }
  }
  for(int i = 1; i <= n; ++i){
    for(auto [v, op] : g[i]){
      if(color[v] != color[i]){
        e[color[i]].push_back({color[v], op});
        in[color[v]]++;
      }
      else{
        w[color[i]] += cost(op);
      }
    }
  }
  vector<int> pq;
  for(int i = 1; i <= tot; ++i){
    dp[i] = -1e18;
    if(!in[i]){
      pq.push_back(i);
    }
  }
  cin >> s;
  dp[color[s]] = w[color[s]];
  while(pq.size()){
    int i = pq.back();
    pq.pop_back();
    for(auto [x, op] : e[i]){
      dp[x] = max(dp[x], dp[i] + w[x] + op);
      in[x]--;
      if(!in[x]){
        pq.push_back(x);
      }
    }
    ans = max(ans, dp[i]);
  }
  cout << ans;
  return 0;
}

posted @ 2024-04-11 17:56  liuyichen  阅读(7)  评论(0编辑  收藏  举报