[ARC133B] Dividing Subsequence

Dividing Subsequence

这道题与最长公共子序列类似,可以先去水一水那道题。

题意

本题就是让你从 \(p\) 里面选出一个子序列 \(b_i\)\(q\) 里面选出一个子序列 \(a_i\),我们要使 \(b_i\)\(a_i\) 的倍数。

解法

本题直接用动态规划,是 \(O(n^2)\) 做法,会超时,因此我们用树状数组维护区间最值来进行优化,那么本题与最长上升子序列的解法类似——因为我们求了前面的可以求后面的,但是求了后面的就不能求前面的了,而且还要求最长,因此是一个最长上升子序列问题。注意对于 \(p_i\) 更新 \(q_i\) 时要从大到小。

解释很多在代码上,请看代码:

代码

// Problem: [ARC133B] Dividing Subsequence
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/AT_arc133_b
// Memory Limit: 1 MB
// Time Limit: 5000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;

#define lowbit(x) x & -x
#define pb push_back
#define all(x) x.begin(), x.end()
#define fst ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

typedef long long ll;

const ll N = 1e6 + 10;

int n;     // n个元素
int a[N];  // p数组的每个元素
int b[N];  // q数组的每个元素
int c[N];  // c[]表示i元素在q数组中的位置

vector<int> v[N];  //存储i元素所有的倍数位置
int d[N];
void update(int x, int v) {
  while (x <= n) {
    d[x] = max(d[x], v);
    x += lowbit(x);
  }
}
int query(int x) {
  int res = 0;
  while (x) {
    res = max(d[x], res);
    x -= lowbit(x);
  }
  return res;
}

int main() {
  fst;
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  // 3 1 4 2
  // 2 4 1 3
  for (int i = 1; i <= n; i++) {
    cin >> b[i];
    c[b[i]] = i;
  }
  for (int i = 1; i <= n; i++) {
    for (int j = i; j <= n; j += i) {
      //对于i元素,i的倍数是j
      v[i].pb(c[j]);
    }
  }
  for (int i = 1; i <= n; i++) {
    //目标维护a[i]的所有倍数出现的位置
    //对这个位置进行降序排列
    int t = a[i];
    sort(all(v[t]));
    reverse(all(v[t]));
    //就是求这个的最长上升子序列
    for (auto e : v[t]) {
      //对于e元素来说,求来的比我早的,且数值比我小的最大值
      int tmp = query(e - 1);
      update(e, tmp + 1);
    }
  }
  cout << query(n) << endl;
  return 0;
}

本题就愉快地结束了。

posted @ 2024-02-21 21:05  zhuwen  阅读(6)  评论(0编辑  收藏  举报