2018多校集训 H. Hills And Valleys

传送门

题意

给你一个\(n \leq 10^5\)的序列,数字都是0-9,你可以任意翻转一个子区间,问翻转后最长不降子序列的最大长度。

题解

简略题解:我开始考虑的是\(f_i,j,k\)表示的是翻转\(i\)结尾的区间,翻转区间贡献了最长不降序列中\(j\)\(k\)的部分, 那么只要新加入的数小于\(j\)就可以翻过去,大于\(k\)就可以补到后面,但其实有问题,因为你不知道翻转区间之前的区间贡献的范围,所以要记录\(f_i,j,k,l\)多一个状态表示翻转区间之前贡献了\(1-l\),但这样会爆炸空间,我不知道能不能滚动数组,我最后考虑使用性质优化:对于一个翻转区间, 如果区间端点不是贡献最小值,或者贡献最大值,也就是端点不参与贡献,那么翻转一个更小不包括端点的区间必然更优。所以我们考虑直接使得右端点是最大值,那么就可以少存一位,然后枚举上一个最大值就好了,对于每一个最大值,只枚举最近的一个数就好了。

实现

#include <iostream>
#include <cstdio>
#define ll long long 
using namespace std;

int read(){
	int num=0, flag=1; char c=getchar();
	while(!isdigit(c) && c!='-') c=getchar();
	if(c == '-') flag=-1, c=getchar();
	while(isdigit(c)) num=num*10+c-'0', c=getchar();
	return num*flag;
}

int readc(){
	char c=getchar();
	while(!isdigit(c)) c=getchar();
	return c-'0';
}

const int N = 1e5+1000;
const int M = 10;
int n, T;
int a[N];
int f[N][M][M], L[N][M][M];
int g[N][M];
int h[N][M];
int las[N][M];
int main(){
	T = read();
	while(T--){
		n = read();
		for(int i=1; i<=n; i++) a[i]=readc();
		
		for(int i=1; i<=n; i++){
			for(int j=0; j<M; j++) las[i][j] = las[i-1][j];
			if(i != 1) las[i][a[i-1]] = i-1;
		}
		
		for(int i=0; i<=n+1; i++){
			for(int j=0; j<M; j++){
				g[i][j] = 0;
				h[i][j] = 0;
				for(int k=0; k<M; k++){
					f[i][j][k] = 0;
				} 
			}
		} 
		
		for(int i=1; i<=n; i++){
			for(int j=0; j<M; j++){
				g[i][j] = g[i-1][j];
				if(a[i] <= j) g[i][j] = max(g[i][j], g[i-1][a[i]] + 1);
			}
		} 
		
		for(int i=1; i<=n; i++){
			for(int j=0; j<M; j++){
				for(int k=j; k<M; k++){
					f[i][j][k]=0,L[i][j][k]=i;
					if(a[i]<=k && a[i]>=j) {
						f[i][j][k]=g[i-1][j]+1;
						
						for(int l=k; l>=a[i]; l--){
							int nex = las[i][l];	
							int res=f[nex][j][k]+1;
							if(res>f[i][j][k]) f[i][j][k]=res, L[i][j][k]=L[nex][j][k];
						}
					}
					
					
				} 
			}
			
			
		} 
		
		for(int i=n; i>=1; i--){
			for(int j=0; j<M; j++){
				h[i][j] = h[i+1][j];
				if(a[i] >= j){
					h[i][j] = max(h[i][j], h[i+1][a[i]]+1);
				}
			}
		}
		
		int ans=0, ansl=1, ansr=1;
		for(int i=1; i<=n; i++){
			for(int j=0; j<M; j++){
				for(int k=j; k<M; k++){
					if(f[i][j][k] + h[i+1][k] > ans){
						ans=f[i][j][k] + h[i+1][k], ansl=L[i][j][k], ansr=i;
					}
				}
			}
		}
		

		printf("%d %d %d\n", ans, max(1, ansl), ansr);
//		printf("%d\n", ans);
		
		
	}
	return 0;
} 





/*
1
9
203258468
*/

 
posted @ 2024-08-02 10:53  ltdJcoder  阅读(2)  评论(0编辑  收藏  举报