Up Down Subsequence P 题解

Akano 发布于 2023-09-11 3 次阅读


思路

首先还是要推出正常的 DP 方程,它应该是 $n$ 个状态 $O(n^2)$ 复杂度的。个人是正推的这个式子,即:(懒得写状态转移方程直接摆代码了)

if(str[f[i]+1] == 'U'){
  if(arr[j] > arr[i]){
    f[j] = max(f[j],f[i] + 1); 
  }
}else{
  if(arr[j] < arr[i]){
    f[j] = max(f[j],f[i] + 1);
  }
}

考虑逆推,相当于每次算出一个 $f_i$ 就是插入了一个转移,如果 $f_i$ 对应的字符为 U,那么相当于在一个转移序列 up 中插入了 $(arr_i,f_i+1)$,意思是如果有一个数 $u$ 满足 $arr_u \gt arr_i$,那么 $f_u$ 可以用 $f_i+1$ 更新;如果对应字符为 D,那么就是在反过来的 down 中插入同样的值。

显然,如果 up 中存在 $(a,b)$,$(c,d)$ 满足 $a \lt c$ 且 $b \ge d$ 那么后者显然不如前者优(可以更新的范围更小,值还更差),那么我们直接弹出。反之 down 也是同理。

于是我们维护两个单调的转移序列(不知道该怎么叫......)updown,它们的键值对 $(key,val)$ 满足 $key$ 单调递增(反过来当然也可以),$val$ 单调递减。每次转移从这两个转移序列转移,然后把新的 $arr_i,f_i+1$ 插入,然后向右边(或者左边)检测是否不满足单调性,如果是的话则删除。

这个序列使用 set 维护,总复杂度 $O(n \log n)$:转移就使用 lower_bound,显然 $O(\log n)$,而删除操作单次最多 $O(\log n)$,由于一次转移只插入一个值,均摊下来删除操作也是 $O(\log n)$的。

另外很神奇的一点是,咱的代码其实有一点小问题,只用新的转移弹掉不满足单调性的旧的转移,却没有检查新的转移是否不满足单调性。也许新的转移一定更优?不清楚了

代码

#include<bits/stdc++.h>
#define Akano 1
#define pure__Elysia 0
#define loves ^
using namespace std;
const int MAXN = 3e5 + 1018 + 1108;
int n,arr[MAXN],f[MAXN];
string str;
set<pair<int,int> > up,down;
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i = 1;i <= n;i++)cin>>arr[i];
	cin>>str;
	str = "." + str;
	for(int i = 1;i <= n;i++){
		auto it1 = up.lower_bound(make_pair(arr[i],0));
		if(it1 != up.begin()){
			it1--;
			f[i] = max(f[i],(*it1).second);
		}
		auto it2 = down.upper_bound(make_pair(arr[i],0));//upper,lower应该一样的
		if(it2 != up.end()){
			f[i] = max(f[i],(*it2).second);
		}
		if(str[f[i]+1] == 'U'){
			auto it = up.insert(make_pair(arr[i],f[i]+1)).first;
			while(true){
				auto nxt = it;
				nxt++;
				if(nxt == up.end())break;
				if((*nxt).second <= f[i] + 1){
					up.erase(nxt);
				}else{
					break;
				}
			}
		}else{
			auto it = down.insert(make_pair(arr[i],f[i]+1)).first;
			while(it != down.begin()){
				auto pre = it;
				pre--;
				if((*pre).second <= f[i] + 1){
					down.erase(pre);
				}else{
					break;
				}
			}
		}
	}
	int ans = 0;
	for(int i = 1;i <= n;i++){
		ans = max(ans,f[i]);
	}
	cout<<ans;
	return not(Akano loves pure__Elysia);
}
此作者没有提供个人介绍。
最后更新于 2026-09-10