codeforces1468A LaIS
https://codeforces.com/contest/1468/problem/A
这个题是在原本lis的基础上的升级版...
之前一直思考这个题必须得存储最后两个数的信息才能进行状态转移....好吧是我的动态规划还不是太好...
我们还是先考虑最原始的DP,f[i]表示以a[i]结尾的最长的长度。
转移的时候可以考虑分类讨论,以a[i]结尾无外乎两种情况,我们令上一个数为a[j],上上一个为a[k].
1.a[j]<=a[i].在这种情况下,我们发现无论a[k]是什么值,我们都能直接向a[j]后面加上a[i].即
f[i]=max(f[j]+1),(1<=j<=i-1,且a[i]>=a[j])。
2.a[j]>a[i].在这种情况下,我们可以发现a[k]<=a[i]才行。但我们的状态没有记录上上一个数的信息,怎么办?其中一个解决办法就是直接从a[k]转移到a[j],相当于我们直接在a[k]的后面直接加上a[j]与a[i]。发现都是可以加上去的。但我们必须保证他们之间的相对关系是正确的。即:
f[i]=max(f[k]+2),(1<=k<=i-2,且a[i]>=a[k],在[k+1,i-1]之间存在a[j]>=a[i]).
第一个情况,我们可以直接用权值线段树即可。第二种情况,我们可以先找到离a[i]最近的大于等于a[i]的a[j],之后可以在[1,j-1]间找最大值。发现这个题又有权值限制,又有位置限制,可以用主席树进行维护。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int T,n,a[N],tot,root[N],st[N],top;
struct Tree
{
int lc,rc,dat;
#define lc(p) t[p].lc
#define rc(p) t[p].rc
#define dat(p) t[p].dat
}t[N*25];
inline int build(int l,int r)
{
int p=++tot;
dat(p)=0;
if(l==r) return p;
int mid=l+r>>1;
lc(p)=build(l,mid);
rc(p)=build(mid+1,r);
return p;
}
inline int ask(int p,int l,int r,int L,int R)
{
if(L<=l&&R>=r) return dat(p);
int mid=l+r>>1,ans=0;
if(L<=mid) ans=max(ans,ask(lc(p),l,mid,L,R));
if(R>mid) ans=max(ans,ask(rc(p),mid+1,r,L,R));
return ans;
}
inline int insert(int now,int l,int r,int x,int v)
{
int p=++tot;
t[p]=t[now];
if(l==r) {dat(p)=max(dat(p),v);return p;}
int mid=l+r>>1;
if(x<=mid) lc(p)=insert(lc(now),l,mid,x,v);
else rc(p)=insert(rc(now),mid+1,r,x,v);
dat(p)=max(dat(lc(p)),dat(rc(p)));
return p;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);tot=0;top=0;
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
root[0]=build(0,n);
for(int i=1;i<=n;++i)
{
int d=ask(root[i-1],0,n,0,a[i])+1;
while(top&&a[i]>=a[st[top]]) --top;
if(top) d=max(d,ask(root[st[top]-1],0,n,0,a[i])+2);
root[i]=insert(root[i-1],0,n,a[i],d);
st[++top]=i;
}
printf("%d\n",dat(root[n]));
}
return 0;
}