P10611 故事结局 题解

思路#

考虑出题人说的第一种做法。

我们首先将操作用颜色段均摊求一下,可以求出每一段颜色的出现时间。

然后进行线段树分治。

我们将每一段颜色放到线段树上,这样就拆成了 qlogq 个修改。

然后把询问同样放到线段树上,由于询问在时间上是一个单点,所以每个包括这个单点线段树节点都要放,这样也拆成了 qlogq 个询问。

这样有什么好处呢。

在线段树的每一个节点上,我们发现所有的修改都在询问前面,这意味着我们可以先预处理然后修改。

如何求解?

再开一个线段树,我们先把行放到线段树上,这样每一个查询就会被拆成 log 个线段树上的询问,每一个修改由于是单独一行,所以要放到所有包括它的线段树节点上。

那么我们修改也变成了 qlogqlogn 个区间上操作,查询也 qlogqlogn 个变成区间上的查询。

这个如何操作呢?

我想到的做法是使用带权并查集。

我们可以将修改 (l,r,v) 按照 v 从大到小排序。

然后一次从 l 遍历到 r,并赋值,然后用并查集并起来。

然后查询 (l,r) 类似的按照 r 从小到大排序。

然后询问时从 l 遍历到 r,也用并查集并起来。

因为 r 从小到大,所以不会并到不需要访问的节点。

这样就在 α(q) 的时间复杂度处理出来。

这样总的时间复杂度是:O(qlogqlognα(qlogqlogn))

当然实际实现的时候我们是做不到这个复杂度的。

我们的排序或许可以在插入线段树之前排好序,但是我们需要离散化。

加入我们在插入线段树之前离散化,然后把离散化的值放到线段树上。

这样其实还没有在线段树里面排序离散化快。

所以一般实现是可以写 log3 的做法,也能过,但其实没有比正解好写很多。

Code#

/*
  ! 如果没有天赋,那就一直重复
  ! Created: 2024/06/26 20:26:19
*/
#include <bits/stdc++.h>
using namespace std;

#define fro(i, x, y) for (int i = (x); i <= (y); i++)
#define pre(i, x, y) for (int i = (x); i >= (y); i--)

using i64 = long long;

const int N = 4e5 + 10;

int n, m, q, ct, k1, k2, ect, ect2;
i64 s[N], b[N], st[N][20];
int sz[N << 1], head[N << 1];
struct Node {
  int x, l, r, v, L, R, id;
} d1[N];
struct Quer {
  int x, y, l, r, d, id;
} d2[N];
struct Kdlt {
  int l;
  mutable int r, v, ti;
  inline bool operator<(const Kdlt&tmp) const {
    return l < tmp.l;
  }
};
struct edge {
  int to, nxt, val;
} e[N * 32];
set<Kdlt> kdl[N];

namespace IO {
const int I = 1e6; char buf[I], s[I], *p1, *p2;
#define gc() (p1==p2&&(p2=(p1=buf)+cin.rdbuf()->sgetn(buf,I),p1==p2)?EOF:*p1++)
template<typename T> inline void read(T &x) {
  x = 0; int q = 1; char z;
  while(!isdigit(z = gc())) if(z == '-') q = -1;
  while(isdigit(z)) x = x * 10 + (z ^ 48), z = gc(); x *= q;
}
template<typename T, typename ...Args> inline void read(T &x, Args &...args) { read(x), read(args...); }
template<typename T> inline void put(T a) {
  int tp = 0; if(a < 0) cout.rdbuf()->sputc('-'), a = -a;
  if(!a) return cout.rdbuf()->sputc('0'), void();
  while(a) s[++tp] = a % 10 + 48 , a /= 10;
  while(tp) cout.rdbuf()->sputc(s[tp--]);
}
inline void put(const char *a) { while(*a) { cout.rdbuf()->sputc(*(a++)); } }
template<typename T, typename ...Args> inline void put(T x, Args ...args) { put(x), put(args...); }
} using IO::put; using IO::read;
inline i64 ask(int l, int r) {
  int g = __lg(r - l + 1);
  return max(st[l][g], st[r - (1 << g) + 1][g]);
}
inline auto spl(int x, int l) {
  auto it = prev(kdl[x].upper_bound({l}));
  if (it->l == l) return it; int r = it->r;
  return it->r = l - 1, kdl[x].insert({l, r, it->v, it->ti}).first;
}
inline void upd(int x, int l, int r, int v) {
  ++ct;
  auto it1 = spl(x, l), it2 = spl(x, r + 1);
  for (auto i = it1; i != it2; i++)
    if (i->ti) d1[++k1] = {x, i->l, i->r, i->v, i->ti, ct - 1}, d1[k1].id = k1;
  kdl[x].erase(it1, it2);
  kdl[x].insert({l, r, v, ct});
}
namespace sol {
int nt[N], nx[N], f1[N], f2[N], buc[N];
i64 mx[N];
Node g1[20][N];
Quer g2[20][N];
inline void upd(int p, int l, int r, int L, int R, int k) {
  if (L <= l && r <= R) return e[++ect] = {k, head[p], 1}, head[p] = ect, void();
  int mid = (l + r) >> 1;
  if (mid >= L) upd(mid<<1, l, mid, L, R, k);
  if (mid <  R) upd(mid<<1|1, mid + 1, r, L, R, k);
}
inline void upd(int p, int l, int r, int k, int x) {
  e[++ect] = {x, head[p], 2}, head[p] = ect;
  if (l == r) return;
  int mid = (l + r) >> 1;
  if (mid >= k) upd(mid<<1, l, mid, k, x);
  if (mid <  k) upd(mid<<1|1, mid + 1, r, k, x);
}
inline int gf1(int x) {
  return nt[x] == x ? x : (nt[x] = gf1(nt[x]));
}
inline int gf2(int x, i64 &v) {
  if (nt[x] == x) return x;
  return nt[x] = gf2(nt[x], v), mx[x] = v = max(v, mx[x]), nt[x];
}
inline void sol(int p, int l, int r, int R1, int R2, int dep) {
  if (R1 == 0 || R2 == 0) return;
  int t1 = 0, t2 = 0;
  fro(i, 1, R1) f1[++t1] = g1[dep][i].id;
  fro(i, 1, R2) if (g2[dep][i].x <= l && r <= g2[dep][i].y) f2[++t2] = g2[dep][i].id;
  if (t1 != 0 && t2 != 0) {
    int tt = 0;
    fro(i, 1, t1) buc[++tt] = d1[f1[i]].l, buc[++tt] = d1[f1[i]].r + 1;
    fro(i, 1, t2) buc[++tt] = d2[f2[i]].l, buc[++tt] = d2[f2[i]].r + 1;
    sort(buc + 1, buc + tt + 1);
    tt = unique(buc + 1, buc + tt + 1) - buc - 1;
    fro(i, 1, tt) nt[buc[i]] = buc[i], mx[buc[i]] = 0, nx[buc[i]] = buc[i + 1];
    nx[buc[tt]] = n + 2;
    fro(i, 1, t1) {
      int l = d1[f1[i]].l, r = d1[f1[i]].r + 1, v = d1[f1[i]].v;
      for (int j = gf1(l); j < r; j = gf1(j)) {
        if (!mx[j]) mx[j] = v * ask(j, nx[j] - 1);
        j = nt[j] = nx[j];
      }
    }
    fro(i, 1, tt) nt[buc[i]] = buc[i];
    fro(i, 1, t2) {
      int l = d2[f2[i]].l, r = d2[f2[i]].r + 1, d = f2[i]; i64 v;
      for (int j = gf2(l, v = -1e9); j < r; j = gf2(j, v = -1e9)) {
        j = nt[j] = nx[j];
      }
      gf2(l, v = -1e9), s[d] = max(s[d], v);
    }
  }
  if (l != r) {
    int mid = (l + r) >> 1, r1, r2;
    r1 = r2 = 0;
    fro(i, 1, R1)
      if (g1[dep][i].x <= mid) g1[dep + 1][++r1] = g1[dep][i];
    fro(i, 1, R2) {
      if (g2[dep][i].x <= l && r <= g2[dep][i].y) continue;
      if (g2[dep][i].x <= mid) g2[dep + 1][++r2] = g2[dep][i];
    }
    sol(mid<<1, l, mid, r1, r2, dep + 1);
    r1 = r2 = 0;
    fro(i, 1, R1)
      if (g1[dep][i].x > mid) g1[dep + 1][++r1] = g1[dep][i];
    fro(i, 1, R2) {
      if (g2[dep][i].x <= l && r <= g2[dep][i].y) continue;
      if (g2[dep][i].y > mid) g2[dep + 1][++r2] = g2[dep][i];
    }
    sol(mid<<1|1, mid + 1, r, r1, r2, dep + 1);
  }
}
inline void sol(int p) {
  int t1 = 0, t2 = 0; ect2 = 0;
  for (int i = head[p]; i; i = e[i].nxt) {
    int x = e[i].to;
    if (e[i].val == 1) {
      g1[0][++t1] = d1[e[i].to];
    } else {
      g2[0][++t2] = d2[e[i].to];
    }
  }
  if (t1 == 0 || t2 == 0) return;
  sort(g1[0] + 1, g1[0] + t1 + 1, [&](auto x, auto y) { return x.v > y.v; });
  sort(g2[0] + 1, g2[0] + t2 + 1, [&](auto x, auto y) { return x.r < y.r; });
  sol(1, 1, n, t1, t2, 0);
}
inline void dfs(int p, int l, int r) {
  sol(p);
  if (l == r) return;
  int mid = (l + r) >> 1;
  dfs(mid<<1, l, mid);
  dfs(mid<<1|1, mid + 1, r);
}
inline void init() {
  nt[n + 1] = n + 1;
  nt[n + 2] = n + 2;
  fro(i, 1, k1) {
    upd(1, 1, ct, d1[i].L, d1[i].R, i);
  }
  fro(i, 1, k2) {
    if (d2[i].d) {
      upd(1, 1, ct, d2[i].d, i);
    }
  }
  if (ct) dfs(1, 1, ct);
}
}

signed main() {
  ios::sync_with_stdio(0), cin.tie(0);
  read(n, m, q);
  fro(i, 1, m) read(b[i]);
  fro(i, 1, m) st[i][0] = b[i];
  fro(i, 1, 19) {
    fro(j, 1, m - (1 << i - 1)) {
      st[j][i] = max(st[j][i - 1], st[j + (1 << i - 1)][i - 1]);
    }
  }
  fro(i, 1, n) kdl[i].insert({1, m, 0, 0});
  fro(i, 1, q) {
    int op, l, r, x, y, v;
    read(op);
    if (op == 1) {
      read(l, r, x, v);
      upd(x, l, r, v);
    } else {
      read(l, r, x, y);
      d2[++k2] = {l, r, x, y, ct}, d2[k2].id = k2;
    }
  }
  fro(i, 1, n) {
    for (auto j : kdl[i])
      if (j.ti) d1[++k1] = {i, j.l, j.r, j.v, j.ti, ct}, d1[k1].id = k1;
  }
  sol::init();
  fro(i, 1, k2) put(s[i], "\n");
  return 0;
}

作者:JiaY19

出处:https://www.cnblogs.com/JiaY19/p/18273666

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   JiaY19  阅读(7)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示