题解:P15442 [蓝桥杯 2025 国研究生组] 山峰子序列

· · 题解

首先,n\le 50 做法太没有前途了,因此我们可以想想 O(n^3)

我们可以转换一下,一段 (l_i,r_i) 只需要满足:

就是合法的。

不妨令 t=\sum\limits_{j=l_i+1}^{r_i}[a_{j-1}<a_j]-\sum\limits_{j=l_i+1}^{r_i}[a_{j-1}>a_j],则第二个条件相当于 t=0

dp_{i,j,k} 表示以 i 为结尾,当前 t=jk 代表当前是上升状态还是下降状态(上升为 0,下降为 1)的最长子序列长度,则有以下转移:

考虑初始化:a_i 可以是一个子序列的开头,即 dp_{i,0,0}=1

:::info[暴力代码]

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

const int N = 2010;
int dp[N][N][2], a[N];

int main() {
    memset(dp, -0x3f, sizeof(dp));
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        dp[i][0][0] = 1; 
        for (int j = 0; j <= i; j++) {
            for (int k = 1; k < i; k++) {
                if (j && a[k] < a[i]) dp[i][j][0] = max(dp[k][j-1][0]+1, dp[i][j][0]);
                if (a[k] > a[i]) dp[i][j][1] = max({dp[k][j+1][0]+1, dp[k][j+1][1]+1, dp[i][j][1]});
                dp[i][0][0] = max(dp[i][0][0], dp[k][0][1]+1);
            }
        }
        ans = max(ans, dp[i][0][1]);
    }
    cout << ans;
    return 0;
}

:::

考虑优化,注意到 \max(dp_{k,j-1,0}) (k<i,a_k<a_i) 是可以用树状数组维护的(需要离散化),我们开 n 棵树状数组即可。\max(dp_{k,j+1,0},dp_{k,j+1,1}) (k<i,a_k>a_i) 也是同理,只不过是反向的。

AC code: ```cpp #include<bits/stdc++.h> using namespace std; const int N = 2010; int dp[N][N][2], a[N], b[N], tr[N][N], tr2[N][N]; #define lowbit(x) (x & (-x)) inline void update(int x, int k, int *tr) { while (x <= 2000) tr[x] = max(tr[x], k), x += lowbit(x); } inline int query(int x, int *tr) { int res = -1e9; while (x) res = max(res, tr[x]), x ^= lowbit(x); return res; } int main() { memset(dp, -0x3f, sizeof(dp)); memset(tr, -0x3f, sizeof(tr)); memset(tr2, -0x3f, sizeof(tr2)); int n; cin >> n; for (int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i]; sort(b+1, b+n+1); int cnt = unique(b+1, b+n+1)-b-1; for (int i = 1; i <= n; i++) a[i] = lower_bound(b+1, b+cnt+1, a[i])-b; // 离散化 int ans = 0; for (int i = 1; i <= n; i++) { dp[i][0][0] = ans+1; for (int j = 0; j <= i; j++) { if (j) dp[i][j][0] = query(a[i]-1, tr[j-1]) + 1; dp[i][j][1] = query(2000-a[i], tr2[j+1]) + 1; // 2000-a[i]=2000-a[i]+1-1,为反向查询 update(a[i], dp[i][j][0], tr[j]); update(2000-a[i]+1, max(dp[i][j][0], dp[i][j][1]), tr2[j]); // 2000-a[i]+1 为反向更新 } ans = max(ans, dp[i][0][1]); } cout << ans; return 0; } ```