P5163 WD与地图 题解

Description

CX 让 WD 研究的地图可以看做是 \(n\) 个点,\(m\) 条边的有向图,由于政府正在尝试优化人民生活,他们会废弃一些无用的道路来把省下的钱用于经济建设。

城市都有各自的发达程度 \(s_i\)。为了方便管理,政府将整个地图划分为一些地区,两个点 \(u,v\) 在一个地区当且仅当 \(u,v\) 可以互相到达。政府希望知道一些时刻某个地区的前 \(k\) 发达城市的发达程度总和,以此推断建设的情况。

也就是说,共有三个操作:

1 a b 表示政府废弃了从 \(a\) 连向 \(b\) 的边,保证这条边存在。

2 a b 表示政府把钱用于建设城市 \(a\),使其发达程度增加 \(b\)

3 a b 表示政府希望知道 \(a\) 城市所在地区发达程度前 \(b\) 大城市的发达程度之和。如果地区中的城市不足 \(b\) 个输出该地区所有城市的发达程度总和。

\(n\leq 10^5,m\leq 2\times 10^5,q\leq 2\times 10^5\)

Solution

考虑时空倒流,如果无向图就直接并查集+线段树合并即可。但是这里是 scc,所以无法确定能否合并,并且如果现在一条边的两端点在不同 scc 不代表以后也不在。

注意到一条边如果在某一时刻在 scc 内部,那么以后它一直会在 scc 内部。

如果直接二分,每次把出现时间在 \([1,mid]\) 的边加进去跑 tarjan,但这样做是 \(O(m^2\log m)\),显然过不了。

考虑整体二分,当前时间区间为 \([l,r]\),假设已经用并查集维护好了 \([1,l-1]\) 的 scc 信息,那么对于 \([l,mid]\) 的边就只要在之前的 scc 之间连边跑 tarjan。

对于出现时间在 \([l,mid]\) 的边如果两端点在同一 scc 了,就放 \([l,mid]\),否则放 \([mid+1,r]\)。而出现时间 \(>mid\) 的边显然只能放 \([mid+1,r]\)

至于保证并查集维护好了 \([1,l-1]\) 的 scc 信息,可以每次加完 \([l,mid]\) 的边后先递归右区间,然后用可撤销并查集回到 \(l-1\) 的时刻后再递归左区间即可。

时间复杂度:\(O(m\log^2 m+(m+q)\log m)\)

Code

#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 1e5 + 5, kMaxM = 3e5 + 5;

struct Node {
  int op, a, b;
} qq[kMaxM];

int n, m, q, tot, mm;
int s[kMaxN], t[kMaxM], unq[kMaxM];
std::pair<int, int> ed[kMaxM];

int getid(int x) { return std::lower_bound(unq + 1, unq + 1 + mm, x) - unq; }

void discrete() {
  std::sort(unq + 1, unq + 1 + mm);
  mm = std::unique(unq + 1, unq + 1 + mm) - (unq + 1);
}

namespace GETT {
int dfn[kMaxN], low[kMaxN];
bool ins[kMaxN];
std::stack<std::tuple<int, int, int>> stk;
std::vector<int> vec, G[kMaxN];
std::map<std::pair<int, int>, int> mp;

struct DSU {
  int fa[kMaxN], rnk[kMaxN];

  void init(int n) {
    std::fill_n(rnk + 1, n, 1);
    std::iota(fa + 1, fa + 1 + n, 1);
  }

  int find(int x) {
    for (; x != fa[x]; x = fa[x]) {}
    return x;
  }

  void unionn(int x, int y) {
    int fx = find(x), fy = find(y);
    if (fx == fy) return;
    if (rnk[fx] > rnk[fy]) std::swap(fx, fy);
    fa[fx] = fy;
    int det = (rnk[fx] == rnk[fy]);
    rnk[fy] += det;
    stk.emplace(fx, fy, det);
  }
} dsu;

void back(int t) {
  for (; stk.size() > t; stk.pop()) {
    auto [x, y, det] = stk.top();
    dsu.rnk[y] -= det, dsu.fa[x] = x;
  }
}

void dfs(int u) {
  static int cnt = 0;
  static std::stack<int> stk;
  dfn[u] = low[u] = ++cnt, stk.emplace(u), ins[u] = 1;
  for (auto v : G[u]) {
    if (!dfn[v]) {
      dfs(v), low[u] = std::min(low[u], low[v]);
    } else if (ins[v]) {
      low[u] = std::min(low[u], dfn[v]);
    }
  }
  if (low[u] == dfn[u]) {
    for (; !stk.empty();) {
      int t = stk.top();
      stk.pop();
      ins[t] = 0, dsu.unionn(t, u);
      if (t == u) break;
    }
  }
}

void solve(int l, int r, std::vector<int> &vec) {
  if (l == r) {
    std::vector<int> idx;
    for (auto i : vec) {
      int x = dsu.find(ed[i].first), y = dsu.find(ed[i].second);
      G[x].emplace_back(y), idx.emplace_back(x), idx.emplace_back(y);
    }
    int lst = stk.size();
    for (auto x : idx) {
      if (!dfn[x]) dfs(x);
    }
    for (auto i : vec) {
      if (dsu.find(ed[i].first) == dsu.find(ed[i].second)) {
        t[i] = l;
      }
    }
    back(lst);
    for (auto x : idx) G[x].clear(), dfn[x] = low[x] = ins[x] = 0;
    return;
  }
  int mid = (l + r) >> 1, lst = stk.size();
  std::vector<int> idx, ls, rs;
  for (auto i : vec) {
    if (i <= mid) {
      int x = dsu.find(ed[i].first), y = dsu.find(ed[i].second);
      G[x].emplace_back(y), idx.emplace_back(x), idx.emplace_back(y);
    }
  }
  for (auto x : idx) {
    if (!dfn[x]) dfs(x);
  }
  for (auto i : vec) {
    if (i <= mid) {
      if (dsu.find(ed[i].first) == dsu.find(ed[i].second)) {
        ls.emplace_back(i);
      } else {
        rs.emplace_back(i);
      }
    } else if (dsu.find(ed[i].first) != dsu.find(ed[i].second)) {
      rs.emplace_back(i);
    }
  }
  for (auto x : idx) G[x].clear(), dfn[x] = low[x] = ins[x] = 0;
  solve(mid + 1, r, rs);
  back(lst);
  solve(l, mid, ls);
}

void solve() {
  for (auto [p, o] : mp) {
    if (o) ed[++tot] = p;
  }
  std::reverse(ed + 1, ed + 1 + tot);
  std::fill_n(t + 1, tot, tot + 1);
  for (int i = 1; i <= tot; ++i) vec.emplace_back(i); 
  dsu.init(n);
  solve(1, tot, vec);
}
} // namespace GETT

namespace GETANS {
int sgt_tot, id[kMaxM], fa[kMaxN], rt[kMaxN], ls[kMaxN * 32], rs[kMaxN * 32], sum[kMaxN * 32], cnt[kMaxN * 32];
std::vector<std::tuple<int, int, int>> vec;

void pushup(int x) {
  sum[x] = sum[ls[x]] + sum[rs[x]];
  cnt[x] = cnt[ls[x]] + cnt[rs[x]];
}

void update(int &x, int l, int r, int ql, int v) {
  if (!x) x = ++sgt_tot;
  sum[x] += v * unq[ql], cnt[x] += v;
  if (l == r) return;
  int mid = (l + r) >> 1;
  if (ql <= mid) update(ls[x], l, mid, ql, v);
  else update(rs[x], mid + 1, r, ql, v);
}

int merge(int x, int y, int l, int r) {
  if (!x || !y) return x + y;
  if (l == r) {
    sum[x] += sum[y], cnt[x] += cnt[y];
    return x;
  }
  int mid = (l + r) >> 1;
  ls[x] = merge(ls[x], ls[y], l, mid), rs[x] = merge(rs[x], rs[y], mid + 1, r);
  pushup(x);
  return x;
}

int query(int x, int l, int r, int k) {
  if (!x) return 0;
  else if (k >= cnt[x]) return sum[x];
  else if (l == r) return k * unq[l];
  int mid = (l + r) >> 1;
  if (k <= cnt[rs[x]]) return query(rs[x], mid + 1, r, k);
  else return sum[rs[x]] + query(ls[x], l, mid, k - cnt[rs[x]]);
}

int find(int x) {
  return x == fa[x] ? x : fa[x] = find(fa[x]);
}

void unionn(int x, int y) {
  int fx = find(x), fy = find(y);
  if (fx != fy) fa[fx] = fy, rt[fy] = merge(rt[fy], rt[fx], 1, mm);
}

void solve() {
  int now = tot;
  for (int i = 1; i <= tot; ++i) {
    vec.emplace_back(t[i], ed[i].first, ed[i].second);
  }
  std::sort(vec.begin(), vec.end());
  for (int i = 1; i <= q; ++i) {
    id[i] = now;
    if (qq[i].op == 1) --now;
  }
  for (int i = 1; i <= n; ++i) {
    fa[i] = i, update(rt[i], 1, mm, getid(s[i]), 1);
  }
  int pos = -1;
  std::vector<int> ans;
  for (int i = q; i; --i) {
    for (; pos + 1 < vec.size() && std::get<0>(vec[pos + 1]) <= id[i];) {
      auto [t, u, v] = vec[++pos];
      unionn(u, v);
    }
    if (qq[i].op == 2) {
      int fi = find(qq[i].a);
      update(rt[fi], 1, mm, getid(s[qq[i].a]), -1);
      s[qq[i].a] -= qq[i].b;
      update(rt[fi], 1, mm, getid(s[qq[i].a]), 1);
    } else if (qq[i].op == 3) {
      int fi = find(qq[i].a);
      ans.emplace_back(query(rt[fi], 1, mm, qq[i].b));
    }
  }
  std::reverse(ans.begin(), ans.end());
  for (auto x : ans) std::cout << x << '\n';
}
} // namespace GETANS

void dickdreamer() {
  std::cin >> n >> m >> q;
  for (int i = 1; i <= n; ++i) {
    std::cin >> s[i];
    unq[++mm] = s[i];
  }
  for (int i = 1; i <= m; ++i) {
    int u, v;
    std::cin >> u >> v;
    GETT::mp[{u, v}] = 1;
  }
  for (int i = 1; i <= q; ++i) {
    std::cin >> qq[i].op >> qq[i].a >> qq[i].b;
    if (qq[i].op == 1) {
      ed[++tot] = {qq[i].a, qq[i].b}, GETT::mp[{qq[i].a, qq[i].b}] = 0;
    } else if (qq[i].op == 2) {
      s[qq[i].a] += qq[i].b;
      unq[++mm] = s[qq[i].a];
    }
  }
  discrete(), GETT::solve(), GETANS::solve();
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}
posted @ 2024-02-20 10:49  下蛋爷  阅读(35)  评论(0)    收藏  举报