CF10D LCIS(DP 状态设计,LIS,LCS,PST,ATE,VAI,调整转移顺序)

看到题只能想到离散化。

纯粹暴力

\(f(i,j,k)\) 表示用去 \(A_i,B_j\) 且以 \(k\) 结尾答案。

\[f(i,j,k)=\min\limits_{k'<k}\set{f(i-1,j,k),f(i,j-1,k),\mathrm{[if\ A_i=B_j=k]} f(i-1,j-1,k')+1} \]

这真的非常暴力了,空间三次方,时间四次方。

update 2026/2/22
其实并非纯粹,其实这里隐含着 ATE 思想,
这里不是通过观察转移性质,而是通过优化状态设计的方式,
下面之所以不能勒令两个,就是要通过一维 ATE 保证不需要循环去做,
总而节省了时间复杂度。

优化状态设计 I

首先可以优化一下状态设计,以 \(k\) 结尾答案改为以不超过 \(k\) 为结尾,效果是一样的,性质和速度就来了。

\[f(i,j,k)=\min\limits_{k'<k}\set{f(i-1,j,k),f(i,j-1,k),f(i,j,k-1),\mathrm{[if\ A_i=B_j\le k]} f(i-1,j-1,A_i-1)+1} \]

时空皆是三次方,记录不难,完全等同于 AT-DP LCS 那一道题。

update 2026/2/22
同样 ATE 思想。

优化状态设计 II

但有个问题,空间不够。
我们当然可以压缩掉 \(i\) 这一维,但路径输出离不开他。

小性质(真的吗?):输出路径不支持常规内存压缩方式。

发现我们可以勒令 \(B_j=k\) 去设计,于是内存变为平方,状态同时减少。

\[f(i,j)=\max\set{\mathrm{[if\ A_i=B_j]}\max\limits_{j'<j,B_{j'}<B_j}\set{f(i-1,j')+1},f(i-1,j)} \]

转移优化:Planning for the Same Task

对于同一个 \(i\)\(A_i\) 相同,所以求 \(\mathrm{[if\ A_i=B_j]}\max\limits_{j'<j,B_{j'}<B_j}\set{f(i-1,j')+1}\) 除了 \(j'<j\) 的时间戳限制外本质相同。
定义其为 \(\text{the Same Task}\),所谓 \(\text{Plan}\),就是把循环分隔开,用前缀累计的方式去求解,本质上是广义前缀和。
类似的还有求和,求 \(\max/\min\) 等等。

放到这道题上,就是在过程中统计,由于 \(A_i\) 不变,循环中一个变量顺便记录一下即可。
时空复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;
const int N=606;
int n,m,A[N],B[N];
vector<int> busket;
struct notebook{int a,b,x,dp;} k[N][N],tmp;
stack<int> ans;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&A[i]),busket.push_back(A[i]);
    scanf("%d",&m);
    for(int i=1;i<=m;i++) scanf("%d",&B[i]),busket.push_back(B[i]);

    sort(busket.begin(),busket.end());
    auto End=unique(busket.begin(),busket.end());
    for(int i=1;i<=n;i++) A[i]=lower_bound(busket.begin(),End,A[i])-busket.begin()+1;
    for(int i=1;i<=m;i++) B[i]=lower_bound(busket.begin(),End,B[i])-busket.begin()+1;

    for(int i=1,pos,Plan;i<=n;i++){
        Plan=pos=0;
        for(int j=1;j<=m;j++){
            k[i][j]={i-1,j,0,k[i-1][j].dp};
            if(A[i]==B[j]&&k[i][j].dp<Plan+1)
                k[i][j]={i-1,pos,A[i],Plan+1};
            if(A[i]>B[j]&&Plan<k[i-1][j].dp)
                pos=j,Plan=k[i-1][j].dp;
        }
    }

    int best=1;
    for(int i=2;i<=m;i++) if(k[n][best].dp<k[n][i].dp) best=i;
    printf("%d\n",k[n][best].dp);
    for(int i=n,j=best;i>0&&j>0;){
        if(k[i][j].x) ans.push(busket[k[i][j].x-1]);
        tmp=k[i][j],i=tmp.a,j=tmp.b;
    }
    while(!ans.empty()) printf("%d ",ans.top()),ans.pop();
    return 0;
}

二倍经验 P10954。

#include<bits/stdc++.h>
using namespace std;
const int N=3106;
int n,A[N],B[N],dp[N][N];
vector<int> busket;
stack<int> ans;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&A[i]),busket.push_back(A[i]);
    for(int i=1;i<=n;i++) scanf("%d",&B[i]),busket.push_back(B[i]);

    sort(busket.begin(),busket.end());
    auto End=unique(busket.begin(),busket.end());
    for(int i=1;i<=n;i++) A[i]=lower_bound(busket.begin(),End,A[i])-busket.begin()+1;
    for(int i=1;i<=n;i++) B[i]=lower_bound(busket.begin(),End,B[i])-busket.begin()+1;

    for(int i=1,Plan;i<=n;i++){
        Plan=0;
        for(int j=1;j<=n;j++){
            dp[i][j]=dp[i-1][j];
            if(A[i]==B[j])
                dp[i][j]=max(Plan+1,dp[i][j]);
            else if(A[i]>B[j])
                Plan=max(Plan,dp[i-1][j]);
        }
    }

    int ans=-1;
    for(int i=1;i<=n;i++) ans=max(ans,dp[n][i]);
    printf("%d\n",ans);
    return 0;
}

总结:
首先考虑到状态只能设二维,先不考虑 VAI 情况,
至多有一维实现 ATE,另一维必须卡紧,否则需要加入一维。
于是设状态 \(f(i,j)\) 表示卡住 \(A_i\) 对于 \(B_j\) 及其以前 ATE。
那么写出转移:
\(f(i,j)=\max\set{f(i,j-1),\mathrm{[if\ A_i=B_j]}\max\limits_{i'<i,A_{i'}<A_i} f(i',j-1)+1}\)
注意到 j 始终在变,直接转移无法实现 PST,
于是更改转移顺序,先枚举 \(j\) 后枚举 \(i\)\(A_i\) 不变,可以 PST。

VAI:他死了吗?

\(f(i,j)\) 表示卡 \(A_i\) 答案为 \(j\) 对应 \(B\) 最小位置。
\(f(i,j)=nxt_{A_i\ in\ B}\set{\min\limits_{i'<i,A_{i'}<A_i}(f(i',j-1))}\)
树状数组貌似 \(O(n^2\log n)\) 能过。

进一步优化状态设计,进行 ATE,(预先离散化)
\(f(i,j)\) 表示枚举至当前位置,结尾不大于 \(i\) 且答案为 \(j\) 最小 \(B\) 下标。
转移 \(f(A_i,j)=\min \set{(A_i-1,j),f(A_i-1,j-1).nxt}\)
但是这里受 ATE 状态限制需要递推保证单调性,综合来看三次方。

考虑调整转移顺序,先枚举 \(j\),每次转移结束后再统一递推保单调。
综合时间复杂度 \(O(n^2)\)


posted @ 2026-02-08 20:22  2025ing  阅读(1)  评论(0)    收藏  举报