[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;
}
本题就愉快地结束了。