AT_arc195_d [ARC195D] Swap and Erase 题解

我们可以把操作过程分成两个阶段,毕竟先进行全部的交换操作,再进行所有的删除操作,对比一边换一遍删是不会更劣的。

接下来还需要注意到一个结论,一个数至多被交换一次。我们考虑一次交换带来的影响,设相邻的两个数 \(x,y\) 交换后最多可以减少两次操作,即 \(x, y, x, y\) 我们交换中间两个数的情况,那么我们有 \(1\) 的正收益。考虑交换两次是什么样子,我们把 \(x, y, z\) 变成了 \(y, z, x\),最好情况下的排列是 \(y, x, y, z, x\),我们操作了两次但也只减少了两次操作,所以也是不优的,大于两次的也很容易这么贪心的证出来。

基于这个事实,考虑有 \(dp_{i,0/1}\) 表示清空前 \(i\) 位,且第 \(a_i, a_{i-1}\) 位不进行/进行交换的最小操作次数。转移讨论:

设命题 \(P = a_i \ne a_{i - 1}, Q = a_i \neq a_{i - 2}, R = a_i \ne a_{i - 3}\)

  • 当不交换时,讨论前一位的交换情况。

\[dp_{i, 0} = \min(dp_{i - 1, 0} + P, dp_{i - 1, 1} + Q) \]

  • 当交换时,基于上面的分析第 \(i - 1\) 位不能再做交换,那么考虑讨论 \(i - 2\) 的交换。

\[dp_{i, 1} = 1 + P + \min(dp_{i - 2, 0} + Q, dp_{i - 2, 1} + R) \]

直接转移即可做到 \(O(n)\)代码

Ex

维护一个序列,每次可以区间加、区间赋值,询问把任意一个区间变成空数列的最小操作次数。\(n, m \le 10^5\)

考虑上面的 dp 实质上在做 \(\min +\) 卷积,考虑 ddp,我们把转移放到矩阵上。

\[\begin{bmatrix} P& Q& +\infty& +\infty\\ +\infty& +\infty& P+Q+1& P+R+1\\ 0& +\infty& +\infty& +\infty\\ +\infty& 0& +\infty& +\infty \end{bmatrix} \begin{bmatrix} dp_{i-1,0}\\ dp_{i-1,1}\\ dp_{i-2,0}\\ dp_{i-2,1} \end{bmatrix} = \begin{bmatrix} dp_{i,0}\\ dp_{i,1}\\ dp_{i-1,0}\\ dp_{i-1,1} \end{bmatrix} \]

那考虑操作分别的意义:

  • 区间加

注意到区间内部的数并不会因为区间加而改变大小关系,只有 \([l, l + 2], [r + 1, r + 3]\) 的会变动,只需要暴力维护这些位置即可,注意到上述的命题 \(P, Q, R\) 都会随着 \(a\) 改变,所以还需要维护一棵支持区间加、区间推平、单点查的线段树。

  • 区间推平

边界依然按照刚才的方法暴力,注意到推平后 \(P = Q = R = 0\),那么中间的矩阵形状是固定的,只需要预处理这样的矩阵的 \(k\) 次方直接 pushdown 即可。

  • 询问

先算出 \(dp_{1,0/1}, dp_{2,0/1}\) 的值,然后乘上区间的矩阵即可。代码

// ubsan: undefined
// accoders
// 如果命运对你缄默, 那就活给他看。
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
typedef long long LL; 
// #define int LL
const int maxn = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int n, m, a[maxn]; 
inline int cmi(int& x, int y) {
  if(y < x) x = y; return x;
}
struct mat {
  int g[4][4];
  inline void E () { 
    for(int i = 0; i < 4; ++ i)
      for(int j = 0; j < 4; ++ j) g[i][j] = INF * (i != j); 
  }  
  mat () { memset(g, 0x3f, sizeof g); }
  inline mat operator * (const mat& b) const {
    mat c;
    for(int i = 0; i < 4; ++ i)
      for(int k = 0; k < 4; ++ k)
        if(g[i][k] != INF) 
          for(int j = 0; j < 4; ++ j) c.g[i][j] = min(c.g[i][j], g[i][k] + b.g[k][j]);
    return c; 
  }
  inline bool operator == (const mat& b) const {
    for(int i = 0; i < 4; ++ i)
      for(int j = 0; j < 4; ++ j) if(g[i][j] != b.g[i][j]) return 0;
    return 1;
  }
  inline void print() {
    for(int i = 0; i < 4; ++ i, cout << '\n')
      for(int j = 0; j < 4; ++ j) {
        if(g[i][j] == INF) cout << -1 << ' ';
        else cout << g[i][j] << ' '; 
      }
  }
} ID; 
inline bool operator != (const mat& a, const mat& b) { return !(a ==  b); }
inline mat make(int P, int Q, int R) {
  mat a;
  a.g[0][0] = P, a.g[0][1] = Q;
  a.g[1][2] = P + Q + 1, a.g[1][3] = P + R + 1;
  a.g[2][0] = a.g[3][1] = 0; 
  return a;
}
mat pw[maxn]; 
inline void init() {
  pw[0].E(), ID.E(); 
  mat e = make(0, 0, 0); 
  for(int i = 1; i <= n; ++ i) pw[i] = pw[i - 1] * e; 
}
namespace sgt1 {
  int as[maxn << 2], ad[maxn << 2];
  inline void ev(int u, int ass, int add) {
    if(ass) as[u] = ass, ad[u] = 0; 
    ad[u] += add;
  }
  inline void dw(int u) {
    ev(u << 1, as[u], ad[u]);
    ev(u << 1 | 1, as[u], ad[u]); 
    as[u] = 0, ad[u] = 0; 
  }
  inline void modf(int u, int l, int r, int ql, int qr, int ass, int add) {
    if(ql <= l && r <= qr) return ev(u, ass, add);
    int mid = l + r >> 1; dw(u); 
    if(ql <= mid) modf(u << 1, l, mid, ql, qr, ass, add);
    if(qr > mid) modf(u << 1 | 1, mid + 1, r, ql, qr, ass, add); 
  }
  inline int Q(int u, int l, int r, int p) {
    if(l == r) return as[u] + ad[u];
    int mid = l + r >> 1; dw(u); 
    if(p <= mid) return Q(u << 1, l, mid, p);
    else return Q(u << 1 | 1, mid + 1, r, p);
  }
}
inline mat fpow(mat a, int b) {
  mat c; c.E();
  for(; b; b >>= 1, a = a * a)  
    if(b & 1) c = c * a;
  return c;
}
namespace sgt2 {
  mat g[maxn << 2];
  mat as[maxn << 2];
  inline void pu(int u) {
    g[u] = g[u << 1 | 1] * g[u << 1 ]; 
  }
  inline void dw(int u, int l, int r) {
    int mid = l + r >> 1;
    if(as[u] != ID) {
      as[u << 1] = g[u << 1] = pw[mid - l + 1];
      as[u << 1 | 1] = g[u << 1 | 1] = pw[r - mid]; 
      as[u].E(); 
    }
  }
  inline void modf1(int u, int l, int r, int p, mat k) {
    if(l == r) return g[u] = k, void();
    int mid = l + r >> 1; dw(u, l, r);
    if(p <= mid) modf1(u << 1, l, mid, p, k);
    else modf1(u << 1 | 1, mid + 1, r, p, k); 
    pu(u); 
  }
  inline void modf2(int u, int l, int r, int ql, int qr) {
    // if(as[u] != ID) return ;
    if(ql <= l && r <= qr) return as[u] = g[u] = pw[r - l + 1], void();
    dw(u, l, r); 
    int mid = l + r >> 1;
    if(ql <= mid) modf2(u << 1, l, mid, ql, qr);
    if(qr > mid) modf2(u << 1 | 1, mid + 1, r, ql, qr);
    pu(u); 
  }
  inline mat Q(int u, int l, int r, int ql, int qr) {
    if(ql <= l && r <= qr) return g[u];
    int mid = l + r >> 1; dw(u, l, r); 
    if(qr <= mid) return Q(u << 1, l, mid, ql, qr);
    if(ql > mid) return Q(u << 1 | 1, mid + 1, r, ql, qr);
    return Q(u << 1 | 1, mid + 1, r, ql, qr) * Q(u << 1, l, mid, ql, qr);
  }
  inline void build(int u, int l, int r) {
    as[u].E(), g[u].E();
    if(l == r) return ;
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r), pu(u); 
  }
}
inline void maintain(int l, int r) {
  for(int i = l - 3; i <= min(n, l + 2); ++ i) {
    if(i >= 1) a[i] = sgt1::Q(1, 1, n ,i);
    if((i - 3 >= 0) && i >= l) {
      int P = a[i] != a[i - 1];
      int Q = a[i] != a[i - 2];
      int R = a[i] != a[i - 3]; 
      sgt2::modf1(1, 1, n, i, make(P, Q, R)); 
    }
  }
  for(int i = r - 2; i <= min(r + 3, n); ++ i) {
    if(i >= 1) a[i] = sgt1::Q(1, 1, n, i); 
    if((i - 3 >= 0) && (i > r)) {
      int P = a[i] != a[i - 1];
      int Q = a[i] != a[i - 2];
      int R = a[i] != a[i - 3]; 
      sgt2::modf1(1, 1, n, i, make(P, Q, R)); 
    }
  }
}
inline void solve0(int l, int r, int x) {
  sgt1::modf(1, 1, n, l, r, 0, x);
  maintain(l, r); 
}
inline void solve1(int l, int r, int x) {
  sgt1::modf(1, 1, n, l, r, x, 0);
  maintain(l, r); 
  if(l + 3 <= r) sgt2::modf2(1, 1, n, l + 3, r); 
}
inline int Q(int l, int r) {
  if(l == r) return 1;
  auto debug = ID;
  a[l] = sgt1::Q(1, 1, n, l), a[l + 1] = sgt1::Q(1, 1, n, l + 1);
  mat ans;
  // al, al+1
  ans.g[0][0] = 1 + (a[l] != a[l + 1]);
  ans.g[1][0] = ans.g[0][0] + 1;
  ans.g[2][0] = 1;
  ans.g[3][0] = INF;
  if(l + 2 <= r) {
    auto G = sgt2::Q(1, 1, n, l + 2, r);
    ans = G * ans;
  }
  return min(ans.g[0][0], ans.g[1][0]); 
}
signed main() {
  freopen("swap.in", "r", stdin);
  freopen("swap.out", "w", stdout);
  ios :: sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  cin >> n >> m;
  init();
  sgt2::build(1, 1, n); 
  for(int i = 1; i <= n; ++ i) {
    cin >> a[i];
    sgt1::modf(1, 1, n, i, i, a[i], 0);
    if(i >= 3) sgt2::modf1(1, 1, n, i, make(a[i] != a[i - 1], a[i] != a[i - 2], a[i] != a[i - 3])); 
  }
  for(int i = 1; i <= m; ++ i) {
    int op, l, r, x;
    cin >> op >> l >> r;
    if(op == 1) {
      cin >> x;
      solve0(l, r, x); 
    } else if (op == 2) {
      cin >> x, solve1(l, r, x);  
    } else {
      cout << Q(l, r) << '\n'; 
    }
  }
  return 0;
}

posted @ 2025-11-20 07:28  Rainsheep  阅读(15)  评论(1)    收藏  举报