首页 > 其他分享 >P5443

P5443

时间:2024-08-19 15:41:08浏览次数:11  
标签:std int siz vector auto P5443 op

感觉有点神秘诶,第一次做操作分块。

和 cdq 要解决的问题挺像的,但处理修改对询问的贡献时无法只与子问题的规模有关,只能把修改变成整块的和散块的,暴力回滚。

算法流程大概是:

对所有操作分块,在每块内:

  • 进入该块时保留前面修改的影响
  • 按照其它喜欢的方式重新排序
  • 如果这是一个询问,暴力把当前块内发生在它之前的修改考虑入内,然后回滚;否则忽略
  • 最后保留所有修改,信息可以针对下一个块的询问有一定压缩

需要,如果修改全部在前面且询问全部在后面,均摊足够快速回答每个询问(可以重排询问),且可以回滚。

大概做不了传统的三维偏序,因为处理原有修改的影响不是很好做。 找到分块题解了。

每处理完一个块重构一次答案数组,因为只有下一个块直接使用这个数组,且只有相对大小顺序是重要的,下一个块只有 \(O(\sqrt{n})\) 大小,数组的大小是 \(O(\sqrt{n} \times \sqrt{n}) = O(n)\)。

好像还有一种方法。

每处理完一个块按第二维重排已经扫过的所有三元组,按第二维重排询问,在查询时一边挪指针一边加入前面整块的元素,取块长为 \(B\),复杂度为 \(O(nB \log n + n^2/B \log n) = O(n \sqrt{n} \log n)\) ,不知道能不能过。

#include <bits/stdc++.h>

struct tp {
  int u, v, w, i;
  tp(int u = 0, int v = 0, int w = 0, int i = 0) : u(u), v(v), w(w), i(i) {}
};

struct opt {
  int op, x, y, t;
  opt(int op = 0, int x = 0, int y = 0, int t = 0) : op(op), x(x), y(y), t(t) {}
};

const int M = 1e5 + 1;

int fa[M], siz[M];
inline int find(int x) { return fa[x] == x ? x : find(fa[x]); }

int main() {
  double st = clock();
  int n, m; scanf("%d %d", &n, &m);
  std::vector<tp> e(m);
  for (int cnt = 0; auto &[u, v, w, i] : e) {
    scanf("%d %d %d", &u, &v, &w), --u, --v, i = cnt++;
  }
  int q; scanf("%d", &q);
  int B = 1100;
  for (; q; ) {
    int s = std::min(q, B);
    std::vector<opt> a, c;
    std::vector<int> vis(m);
    for (int i = 0, op, x, y; i < s; i++) {
      scanf("%d %d %d", &op, &x, &y), --x;
      if (op == 1) vis[x] = 1, a.emplace_back(op, x, y, i);
      else c.emplace_back(op, x, y, i);
    }
    std::vector<int> b;
    for (int i = 0; i < m; i++) if (!vis[i]) b.push_back(i);
    std::sort(b.begin(), b.end(), [&](auto x, auto y) -> bool {
      return e[x].w > e[y].w;
    });
    std::sort(c.begin(), c.end(), [&](auto x, auto y) -> bool {
      return x.y > y.y;
    });
    for (int i = 0; i < n; i++)
      fa[i] = i, siz[i] = 1;
    std::vector<std::pair<int, int>> mdf;
    auto merge = [&](int u, int v, bool w) {
      if ((u = find(u)) == (v = find(v))) return;
      if (siz[u] < siz[v]) std::swap(u, v);
      siz[u] += siz[v], fa[v] = u;
      if (w) mdf.emplace_back(u, v);
    };

    int j = 0;
    std::vector<int> ans(s, -1);
    for (const auto &[op, x, y, t] : c) {
      for (; j < (int)b.size() && e[b[j]].w >= y; j++)
        merge(e[b[j]].u, e[b[j]].v, 0);
      std::vector<std::pair<int, int>> tmp;
      for (int k = 0; k < (int)a.size() && a[k].t <= t; k++)
        tmp.emplace_back(a[k].x, e[a[k].x].w), e[a[k].x].w = a[k].y;
      for (int k = 0; k < (int)a.size(); k++) {
        if (e[a[k].x].w >= y)
          merge(e[a[k].x].u, e[a[k].x].v, 1);
      }
      ans[t] = siz[find(x)];
      std::reverse(mdf.begin(), mdf.end());
      for (const auto &[u, v] : mdf)
        siz[u] -= siz[v], fa[v] = v;
      mdf.clear();
      std::reverse(tmp.begin(), tmp.end());
      for (const auto &[x, y] : tmp)
        e[x].w = y;
      tmp.clear();
    }
    q -= s;
    for (const auto &[op, x, y, t] : a) if (op == 1)
      e[x].w = y;
    for (auto x : ans) {
      if (x != -1) printf("%d\n", x);
    }
  }
  std::cerr << (clock() - st) / CLOCKS_PER_SEC;
}

标签:std,int,siz,vector,auto,P5443,op
From: https://www.cnblogs.com/purplevine/p/18367399

相关文章

  • P5443 [APIO2019] 桥梁 题解
    容易得出一种暴力算法:将询问按\(w\)排序,将没有修改的边按\(d\)排序。对于每个询问\((t_i,s_i,w_i)\),做两部分操作(这里\(t\)是时间的意思):将没有修改的边中满足\(d......