首页 > 其他分享 >CF1209G2 Into Blocks (hard version) 题解

CF1209G2 Into Blocks (hard version) 题解

时间:2024-02-28 17:23:40浏览次数:28  
标签:std Blocks int 题解 Into mi kMaxN leq 区间

Description

给你 \(n\) , \(q\),\(n\) 表示序列长度,\(q\) 表示操作次数。

我们需要达成这么一个目标状态:
如果存在 \(x\) 这个元素,那么必须满足所有 \(x\) 元素都必须在序列中连续。

然后你可以进行这么一种操作,将所有的 \(x\) 元素的变为任意你指定的 \(y\) 元素,并且花费 \(cnt[x]\) 的花费,\(cnt[x]\) 代表 \(x\) 元素的个数。

现在有 \(q\) 次询问,每次询问单点修改一个位置的值,求修改完之后最小花费使得序列满足目标状态。

注意:更新不是独立的,之前的更新会保留。

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

Solution

先考虑对于一个序列怎么求答案。

显然是把整个序列先划分成若干个最短的区间使得区间内部里出现过的颜色的所有位置都在这个区间里,答案就是 \(n\) 减每个区间颜色出现次数的最大值。

考虑怎么去刻画这个区间。

先不考虑长度是 \(1\) 的区间,不妨设 \(l_i\) 表示颜色 \(i\) 第一次出现的位置,\(r_i\) 表示颜色 \(i\) 最后一次出现的位置,\(b_i\) 表示位置 \(i\) 被多少个区间 \([l_j,r_j)\) 覆盖,\(w_i\) 表示 \(a_i\) 这个颜色的出现次数。

那么每个合法的区间 \([l,r]\) 一定满足 \(b_l,b_{l+1},\dots,b_{r-1}\) 都不为 \(0\),所以每个 \([l,r]\) 都可以用 \(b\) 数组的最长非零区间表示,由于必定会有一个颜色在 \([l,r]\) 中出现至少 \(2\) 次,所以 \([l,r]\) 对答案的贡献就是 \(\max\{w_l,w_{l+1},\dots,w_{r-1}\}\)。

现在考虑用线段树维护这个东西。

设 \(minb_i\) 表示 \(i\) 这个区间 \(b\) 的最小值,\(maxw_i\) 表示区间内 \(w\) 的最大值,\(sum_i\) 表示区间内的贡献之和。

容易发现 \(b_n=0\),所以 \(minb_1=0\),那么总贡献就是 \(sum_1\)。

但是线段树需要支持合并,所以只要再维护区间前缀还没确定的贡献和后缀还没确定的贡献(这个贡献不能包含左端点)。

修改相当于就是删除或加入 \([l_c,r_c)\),并且每次只要修改每个颜色第一次出现的位置的 \(w\),因为求区间 max 时一定会在第一次出现的位置贡献到。

时间复杂度:\(O(n\log n)\)。

Code

#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 2e5 + 5;

int n, q;
int a[kMaxN];
std::set<int> st[kMaxN];

struct SGT {
  int mi[kMaxN * 4], lmx[kMaxN * 4], rmx[kMaxN * 4], mx[kMaxN * 4], sum[kMaxN * 4], tag[kMaxN * 4];

  void pushup(int x) {
    mi[x] = std::min(mi[x << 1], mi[x << 1 | 1]);
    mx[x] = std::max(mx[x << 1], mx[x << 1 | 1]);
    if (mi[x << 1] < mi[x << 1 | 1]) {
      lmx[x] = lmx[x << 1], rmx[x] = std::max(rmx[x << 1], mx[x << 1 | 1]);
      sum[x] = sum[x << 1];
    } else if (mi[x << 1] > mi[x << 1 | 1]) {
      rmx[x] = rmx[x << 1 | 1], lmx[x] = std::max(lmx[x << 1 | 1], mx[x << 1]);
      sum[x] = sum[x << 1 | 1];
    } else {
      lmx[x] = lmx[x << 1], rmx[x] = rmx[x << 1 | 1];
      sum[x] = sum[x << 1] + sum[x << 1 | 1] + std::max(rmx[x << 1], lmx[x << 1 | 1]);
    }
  }

  void addtag(int x, int v) {
    tag[x] += v, mi[x] += v;
  }

  void pushdown(int x) {
    if (!tag[x]) return;
    addtag(x << 1, tag[x]), addtag(x << 1 | 1, tag[x]);
    tag[x] = 0;
  }

  void update1(int x, int l, int r, int ql, int qr, int v) {
    if (l > qr || r < ql) {
      return;
    } else if (l >= ql && r <= qr) {
      return addtag(x, v);
    }
    pushdown(x);
    int mid = (l + r) >> 1;
    update1(x << 1, l, mid, ql, qr, v), update1(x << 1 | 1, mid + 1, r, ql, qr, v);
    pushup(x);
  }

  void update2(int x, int l, int r, int ql, int v) {
    if (l == r) return void(mx[x] = lmx[x] = v);
    pushdown(x);
    int mid = (l + r) >> 1;
    if (ql <= mid) update2(x << 1, l, mid, ql, v);
    else update2(x << 1 | 1, mid + 1, r, ql, v);
    pushup(x);
  }
} sgt;

void upd(int x, int v) {
  int val = a[x];
  if (!st[val].empty()) {
    sgt.update1(1, 1, n, *st[val].begin(), *prev(st[val].end()) - 1, -1);
    sgt.update2(1, 1, n, *st[val].begin(), 0);
  }
  if (v == 1) st[val].emplace(x);
  else st[val].erase(x);
  if (!st[val].empty()) {
    sgt.update1(1, 1, n, *st[val].begin(), *prev(st[val].end()) - 1, 1);
    sgt.update2(1, 1, n, *st[val].begin(), st[val].size());
  }
}

void dickdreamer() {
  std::cin >> n >> q;
  for (int i = 1; i <= n; ++i) {
    std::cin >> a[i];
    st[a[i]].emplace(i);
  }
  for (int i = 1; i <= 2e5; ++i) {
    if (!st[i].size()) continue;
    sgt.update1(1, 1, n, *st[i].begin(), *prev(st[i].end()) - 1, 1);
    sgt.update2(1, 1, n, *st[i].begin(), st[i].size());
  }
  std::cout << n - sgt.lmx[1] - sgt.rmx[1] - sgt.sum[1] << '\n';
  for (int i = 1; i <= q; ++i) {
    int x, y;
    std::cin >> x >> y;
    upd(x, -1), a[x] = y, upd(x, 1);
    std::cout << n - sgt.lmx[1] - sgt.rmx[1] - sgt.sum[1] << '\n';
  }
}

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;
}

标签:std,Blocks,int,题解,Into,mi,kMaxN,leq,区间
From: https://www.cnblogs.com/Scarab/p/18041128

相关文章

  • 2024牛客寒假算法基础集训营6 题解 ( A,B,C,D,E,I)
    2024牛客寒假算法基础集训营6题解(A,B,C,D,E,I)A 宇宙的终结题意找到\([l,r]\)区间中有多少数恰好等于三个不同素数的乘积思路数据范围很小,可以考虑暴力,遍历\([l,r]\)区间内每个数,拿每个小于当前数的素数一直往下除,判断是否存在能被恰好3个素数整除的情况代码/********......
  • 题解 NKOJ2929 【[THUSC2014] 函数求解】
    代码:#include<iostream>#include<queue>#include<cstdio>#include<cmath>usingnamespacestd;typedefstruct{ intnxt; intend; intdis; doublecost;}Edge;constintN=2e3,M=400+7,K=80800+7;constdoubleep......
  • ABC294 EFG 题解
    E-2xNGrid题意给你一个\(2\timesL\)的网格,但是\(L\)很大,所以用以下形式压缩:将同一个颜色的连续段视为一个整体,那么每一行就可以用若干个二元组\((a_i,b_i)\)表示,其中\(a_i\)为颜色,\(b_i\)为连续段的长度。保证长度\(\le10^5\)。输入以上述形式压缩,现在让你求出......
  • P1110 [ZJOI2007] 报表统计 题解
    考察点:STL的熟练运用。记:\(l_i\)表示序列中不超过\(a_i\)的最大数,\(r_i\)表示序列中超过\(a_i\)的最小数。开一个vectorinsert[N]存储\(a_i\)后面插入的所有数字。首先,我们使用一个multisets1来存储相邻元素的差的绝对值,然后再开一个multisets2来存储当前出......
  • CodeChef Chef and Churus 题解
    对给出的\(n\)个区间分块,设块长为\(B\)。每个块内计算每个位置的贡献(被覆盖次数)。具体地,记\(f_{i,j}\)表示第\(i\)个块第\(j\)个数被覆盖了多少次,这个可以用差分轻松维护。预处理复杂度\(O(\frac{n^2}{B})\)。现在考虑修改。记\(ans_i\)表示块\(i\)的贡献,那么对于......
  • ABC303 G 题解
    区间DP。设\(f_{l,r}\)表示只考虑\([l,r]\),先手得分减后手得分的最大值(并不关心谁是先手谁是后手),区间长度\(len=r-l+1\)。然后对三种情况分别讨论:使用操作一,此时取掉左/右端点的部分先手后手互换,对答案的贡献为\(\max(a_l-f_{l+1,r},a_r-f_{l,r-1})\)。使用操作二,继......
  • P1668 题解
    两种做法。一、最短路题目要求区间数量最小。如果能建出图来,就可以转换为最短路问题。具体地,我们从\(l-1\tor\)连一条长度为\(1\)的边,意味着要多经过\((l-1,r]\)这一个区间。这是左开右闭的形式。现在还有一个问题:通过这种边我们只能到达区间的右端点,如果想向左到达区......
  • P1266 速度限制 题解
    考虑分层图。把图按速度分成\(V\)层,\(f_{i,j}\)表示点\(i\)在第\(j\)层(速度为\(j\))的编号。注意:这里的速度为\(j\)是指到达\(i\)那一条边的速度(\(i\)为终点)。考虑一种naive的建边方式:首先,记当前点为\(u\),速度为\(i\);\(u\)的出边速度为\(j\),长度为\(l\),终点......
  • ABC302 Ex 题解
    首先我们考虑\(v\)固定怎么做。实际上就是ARC111B。考虑建图,对每个\((a_i,b_i)\)建一条无向边,那么问题就变成了:对于每条边都要选择它以及其连接的一个点,最大化选出的点数。很明显可以对每个连通块分开考虑。记当前连通块的点数为\(V\),边数为\(E\)。那么有结论:该连通块对......
  • ABC301 Ex 题解
    题意:你有一张无向连通图,定义\(d(s,t)\)表示从\(s\)到\(t\)所有路径中最大边权的最小值。现在给你三个数\(A_i,S_i,T_i\),让你求出当\(A_i\)这条边的边权\(+1\)时,\(d(S_i,T_i)\)会增加多少。首先考虑一下\(A_j+1\)什么时候会对\(d(S_j,T_j)\)有影响。\(A_j>d(S_......