逐月破星杯

Yaosicheng124發表於2024-10-21

C. 區間排序

題目描述

給定一個陣列 \(A\),你要按照如下方式對 \(A\) 排序:

  1. \(A\) 分割成互不相交的子段,且每個元素恰好屬於一個子段。
  2. 準備一個空陣列 \(B\),按順序把這些子段完整地插入到 \(B\) 中的任意位置。

求至少要分成幾個子段。

思路

很明顯我們會貪心的儘可能長的取子段直到不能取。所以我們來考慮怎麼判斷非法。

  • 如果當前元素小於上個元素,那麼很明顯不能放在同一段。
  • 或者如果當前元素大於原陣列中第一個大於 \(A_l\)\(l\) 是當前區間左端點)的元素,那麼也不能放在同一段。因為當前子段一定會插在其之前,所以不能大於它。

使用 set 維護即可。

空間複雜度 \(O(N)\),時間複雜度 \(O(N\log N)\)

程式碼

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1000001;

int n, a[MAXN], ans;

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
  }
  set<int> s;
  s.insert(0), s.insert(1000001);
  for(int i = 1, j = 1; i <= n; i = j) {
    int x = *s.upper_bound(a[i]);
    for(; j <= n && (i == j || a[j] >= a[j - 1]) && a[j] <= x; ++j) {
    }
    ans++;
    for(int k = i; k < j; s.insert(a[k]), ++k) {
    }
  }
  cout << ans;
  return 0;
}

D. 精準拼接

題目描述

給定兩個長為 \(N\) 的數列 \(A,k\)。你要找出一個最長的且滿足以下條件的數列 \(p_1,p_2,\dots,p_m\)

  • \(1\le p_1<p_2<\dots<p_m\le N\)
  • 對於 \(\forall 1<i\le m\),都有 \(\text{popcount}(A_{p_{i-1}}\text{AND} A_{p_i})=k_{p_i}\)

思路

\(dp_{i}\) 表示以 \(i\) 結尾的滿足條件的子序列的最長長度。

\(maxdp_{i,j,k}\) 表示一個轉移 \(a\rightarrow b\) 滿足 \(A_a\) 二進位制下最高 \(10\) 位為 \(i\)\(A_b\) 二進位制下最低 \(10\) 位為 \(j\)\(A_a,A_b\) 的最低十位的 \(\text{popcount}\)\(k\) 中最大的 \(dp_a\)

我們可以這樣使用 \(maxdp\) 轉移(收集型):

  • 此時明顯 \(j\) 已經確定,所以我們可以列舉 \(i\)
  • 由於我們知道要求 \(k_i\),所以我們還可以求出 \(k\)。直接轉移即可。

並這樣更新 \(maxdp\)

  • 這次是 \(i\) 已經確定,同樣列舉 \(j\)。同理 \(k\) 也確定了,直接更新即可。

空間複雜度 \(O(N+V^2\log V)\),時間複雜度 \(O(NV)\),其中 \(V=2^{10}\)

程式碼

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 100001;

int n, a[MAXN], pop[1 << 20], maxdp[21][1 << 10][1 << 10], pos[11][1 << 10][1 << 10], fa[MAXN], ans, p;

void Print(int x) {
  if(!x) {
    return;
  }
  Print(fa[x]);
  cout << x << " ";
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  freopen("stitch.in", "r", stdin);
  freopen("stitch.out", "w", stdout);
  cin >> n;
  for(int i = 1; i < (1 << 20); ++i) {
    pop[i] = pop[i - (i & -i)] + 1;
  }
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
  }
  for(int i = 1, x, dp; i <= n; ++i) {
    cin >> x;
    dp = 1;
    for(int j = 0; j < (1 << 10); ++j) {
      int v = x - pop[j & (a[i] >> 10)];
      if(v >= 0 && maxdp[v][j][a[i] & ((1 << 10) - 1)] + 1 > dp) {
        dp = maxdp[v][j][a[i] & ((1 << 10) - 1)] + 1, fa[i] = pos[v][j][a[i] & ((1 << 10) - 1)];
      }
    }
    if(dp > ans) {
      ans = dp, p = i;
    }
    for(int j = 0; j < (1 << 10); ++j) {
      int v = pop[a[i] & j];
      if(dp > maxdp[v][a[i] >> 10][j]) {
        maxdp[v][a[i] >> 10][j] = dp, pos[v][a[i] >> 10][j] = i;
      }
    }
  }
  cout << ans << "\n";
  Print(p);
  return 0;
}

相關文章