题意:
最长上升子序列,不过是永久最长上升子序列而且每次变化只能改一个值,也就是说对于 i 和 j,i 处所有值都要小于等于 j 处,j 处所有值都要大于等于 i 处才可以。
50pts 做法
根据题意,我们易得基础 DP:
$$f_i=\max(f_i,f_j+1)$$
条件:
- $maxx_j \leq a_i$
- $minn_i \leq a_j$
- $1 \leq j < i$
这里 f 表示以 i 为结尾最大的序列长度, a 表示该位置初始值,maxn 和 minn 分别表示该位置可以出现的最大值和最小值。
如果您推不出这一点,建议先学线性 DP 。。。
$\mathcal O(n^2)$ 的基础 DP 可以拿到 50pts。
code
#include<bits/stdc++.h>
using namespace std;
int n,m,a[100010],maxx[100001],minn[100001],tot[100001],ans;
namespace AYX
{ inline short main()
{ scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)scanf("%d",&a[i]),maxx[i]=minn[i]=a[i];
for(int i=1;i<=m;++i)
{ int pos,x;
scanf("%d%d",&pos,&x);
maxx[pos]=max(maxx[pos],x);
minn[pos]=min(minn[pos],x);
}
for(int i=1;i<=n;++i)
{ tot[i]=1;
for(int j=1;j<i;++j)
if(a[j]<=minn[i] and maxx[j]<=a[i])tot[i]=max(tot[i],tot[j]+1);
ans=max(ans,tot[i]);
}
printf("%d\n",ans);
return 0;
}
}
signed main()
{return AYX::main();
}
下面是 100pts 做法。
我们发现,对于 dp 转移的条件:
- $maxn_i\le a_i$
- $a_j \le minn_i$
- $j<i$
这实际上构成了三维偏序,这样我们就可以通过 CDQ 分治来优化 DP 的转移。具体实现就是模板用树状数组维护 max 值。
值得注意的一点是 $j<i$ 这个条件,在 sort 过程中,id 的位置会被打乱,这样可能导致后面向前面转移,会 WA 。那么我们怎么办呢? CDQ 时打乱了顺序,我们只需要在本次 CDQ 过程后下一个 CDQ 前把位置变回来即可。
具体实现: sort(p+mid+1,p+r+1,cmp3);
cmp3 : return i.id<j.id;
这样这道题就完美解决了。
code
#include<bits/stdc++.h>
using namespace std;
int n,m,f[1000001],ans,c[1000001],nn;
struct LCIS
{int a,maxn,minn,id;}p[1000001];
namespace AYX
{ inline bool cmp1(LCIS i,LCIS j)
{return i.maxn<j.maxn;}
inline bool cmp2(LCIS i,LCIS j)
{return i.a<j.a;}
inline bool cmp3(LCIS i,LCIS j)
{return i.id<j.id;}
inline void add(int x,int val)
{for(int i=x;i<=n;i+=i&-i)c[i]=max(c[i],val);}
inline int query(int x)
{int res=0;for(int i=x;i;i-=i&-i)res=max(res,c[i]);return res;}
inline void clear(int x)
{for(int i=x;i<=n;i+=i&-i)c[i]=0;}
inline void CDQ(int l,int r)
{ if(l==r){f[p[l].id]=max(f[p[l].id],1);return ;}
int mid=(l+r)>>1;
CDQ(l,mid);
sort(p+l,p+mid+1,cmp1);
sort(p+mid+1,p+r+1,cmp2);
int j=l;
for(int i=mid+1;i<=r;++i)
{ while(j<=mid and p[j].maxn<=p[i].a)add(p[j].a,f[p[j].id]),++j;
f[p[i].id]=max(f[p[i].id],query(p[i].minn)+1);
}
for(int i=l;i<j;++i)clear(p[i].a);
sort(p+mid+1,p+r+1,cmp3);
CDQ(mid+1,r);
}
inline short main()
{ scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)scanf("%d",&p[i].a),p[i].maxn=p[i].minn=p[i].a,p[i].id=i;
for(int i=1;i<=m;++i)
{ int pos,x;
scanf("%d%d",&pos,&x);
p[pos].maxn=max(p[pos].maxn,x);
p[pos].minn=min(p[pos].minn,x);
}
CDQ(1,n);
for(int i=1;i<=n;++i)ans=max(ans,f[i]);
printf("%d\n",ans);
return 0;
}
}
signed main()
{return AYX::main();
}