ARC180 补。

A - ABA and BAB

给定一个长度为 \(n\) 的 AB 串 \(s\),进行任意次操作,求可能的结果个数。

操作:将字串 ABA 改为 A,或 BAB 改为 B。

Solution

愣了我一下。注意到连续的 A 或者 B 都无法进行操作,可以被操作的只有 AB 交替的子串。这种子串只能不断删除 AB,可能结果个数为 \(\lceil \frac{s}{2}\rceil\)。可以将 \(s\) 拆分为若干个 \(t\),这些 \(t\) 是相互独立的,所以直接乘即可。

\(\\\)

Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 2.5e5 + 1, kP = 1e9 + 7;

int n;
string s, t;
int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> n >> s;
  LL ans = 1;
  char pre = '!';
  for (int i = 0; i < n; i++) {
    if (s[i] == pre) {
      ans = ans * ((t.size() + 1) / 2) % kP;
      t.clear();
    }
    t += s[i], pre = s[i];
  }
  ans = ans * ((t.size() + 1) / 2) % kP;
  cout << ans << '\n';
  return 0; 
}

B - Improve Inversions

给定一个 \(n\) 排列 \(p\) 和一个正整数 \(k\),求最大操作次数。

操作:交换一个间距至少为 \(k\) 的逆序对。

Solution

对于 \(p\) 作他的逆排列 \(q_{p_i} = i\),题目转为求差至少为 \(k\) 的逆序对。

从左到右一点点吃干抹尽显然不劣。

\(\\\)

Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 500 + 1;

int n, k;
int p[kN], iv[kN];
vector<PII> ans;
int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin >> n >> k;
  for (int i = 1; i <= n; i++) {
    cin >> p[i];
    iv[p[i]] = i;
  }
  for (int i = 2; i <= n; i++) {
    for (int j = i - 1; j >= 1; j--) {
      if (iv[i] + k <= iv[j]) {
        ans.emplace_back(iv[i], iv[j]);
        swap(iv[i], iv[j]);
        i = j;
      }
    }
  }
  cout << ans.size() << '\n';
  for (auto [x, y] : ans) {
    cout << x << ' ' << y << '\n';
  }
  return 0;
}

C - Subsequence and Prefix Sum

给定一个长度为 \(n\) 的序列 \(a\),求进行一次操作后可能的结果个数。

操作:选出 \(a\) 的一个子序列并作前缀和。

Solution

不难想到令 \(f_{i, j}\) 表示考虑到 \(i\),子序列总和为 \(j\) 的方案数。

但是直接转移是错误的。问题出在当被转移的 \(j = 0\) 时,你这个点转移不转移都不会改变,但是会改变转移后的 \(j\),不好判。

pharital 说当 \(j = 0\) 的情况可以拉出来单独转移。

\(g_i\) 表示 \(i\) 这个数值作子序列结尾,子序列前面的和为 \(0\) 的方案数。于是可以 \(f_{i - 1, 0} \Rightarrow g_{a_i},g_{j} \Rightarrow f_{i, j + a_i}\).

转移 \(f\) 的同时特判转移 \(g\) 即可。

\(\\\)

Code

pharital 提供了一个支持负数下标的安全性未知的神秘写法。

// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <atcoder/modint>
#include <iostream>
#include <numeric>
#include <vector>

using namespace std;
using namespace atcoder;
using mint = modint1000000007;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 100 + 1, kS = 1e3 + 1;

int n, a[kN];
mint F[kN][2 * kS], *f[kN], G[2 * kS], *g = G + kS;
int main() {
  for (int i = 0; i < kN; i++) {
    f[i] = F[i] + kS;
  }

  cin.tie(0)->sync_with_stdio(0);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  f[0][0] = 1;
  int l = 0, r = 0;
  for (int i = 1; i <= n; i++) {
    for (int j = l; j <= r; j++) {
      f[i][j] += f[i - 1][j];
      if (j != 0) {
        f[i][j + a[i]] += f[i - 1][j] + g[j];
      }
    }
    g[a[i]] = f[i - 1][0];
    l += min(a[i], 0), r += max(a[i], 0);
  }
  mint ans = 0;
  for (int i = l; i <= r; i++) {
    ans += f[n][i];
  }
  cout << ans.val() << '\n';
  return 0;
}
posted @ 2024-07-27 11:49  Lightwhite  阅读(17)  评论(0)    收藏  举报