如何判断某个位置是不是LIS或者反LIS的数?
如何判断某个位置是不是LIS或者反LIS的数?
笔者是在前几天的abc354这一场的f题发现的,最长上升子序列很显然,我们在求的过程中,设 \(dp[i]\) 为以i位置结尾的最长上升序列的长度,然后求一边所有的最长长度即可,那么对于这种定义,我们考虑一个反定义,即:\(dp2[j]\) 为以j位置开头的最长上升序列的长度,那么显然有这样一个定义:若我们已知最长上升子序列长度,那么我们一定会有 \(dp[i]+dp[2]-1=max(dp[j])\) 这里的 \(dp[j]\) 就是最长上升子序列的长度,这个定义是显然的,所以我们求两次 \(Lis\) 即可
对于以 \(i\) 结尾的,正常求即可,对于以 \(i\) 开头的,我们直接倒着求一边最长下降子序列即可,由于数据范围较大,不可直接用动归,这里笔者选择了二分贪心的思路去做,其他的做法还有线段树和树状数组均可,笔者在后面会补
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
void solve(){
int n; cin>>n;
vector<int>q(n+1),a(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
q[0]=-2e9;
int num=0;
vector<int>lis(n+1);
for(int i=1;i<=n;i++){
if(a[i]>q[num]) q[++num]=a[i],lis[i]=num;
else{
int l=1,r=num;
while(l<r){
int mid=l+r>>1;
if(a[i]<=q[mid]) r=mid;
else l=mid+1;
}
q[r]=a[i],lis[i]=r;
}
}
reverse(a.begin()+1,a.end());
vector<int>_lis(n+1);
q[0]=2e9,num=0;
for(int i=1;i<=n;i++){
if(a[i]<q[num]) q[++num]=a[i],_lis[n-i+1]=num;
else{
int l=1,r=num;
while(l<r){
int mid=l+r>>1;
if(a[i]>=q[mid]) r=mid;
else l=mid+1;
}
q[l]=a[i],_lis[n-i+1]=l;
}
}
vector<int>res;
for(int i=1;i<=n;i++){
if(lis[i]+_lis[i]-1==num)
res.push_back(i);
}
cout<<res.size()<<'\n';
for(auto x:res) cout<<x<<' ';
cout<<'\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int t;cin>>t;while(t--)solve();
}