题意:
玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可。每种变化最多只有一个值发生变化。
题解:
设Max[i]为a[i]可能变成的最大值,Min[i]为a[i]可能变成的最小值(Max[i]和Min[i]均包括a[i]本身)。那么i,j(i<j)连在一起的条件可以很容易得出:即a[i]<=Min[j]且Max[i]<=a[j]。很好理解,如果i变了,那么考虑极端情况,变成最大值也要满足条件,如果j变了,那么同样考虑极端情况,变成最小值也要满足条件。
这样我们就可以dp了,f[i]表示以i结尾的最长合法序列,那么f[i]=f[j]+1(j满足上述条件)。这实际上是一个三维偏序,所以可以使用树套树维护,外层为a[i],内层为Max[i],使用树状数组套线段树维护即可解决问题。
#include<cstdio> #include<algorithm> #include<cstdlib> using namespace std; const int INF=100000; int n,m,a[100002],Max[100002],Min[100002],rt[100002],cnt,f[100002],ans; typedef struct{ int ls,rs,Max; }P; P p[10000002]; void gengxin(int root,int begin,int end,int wz,int z){ if (begin>wz || end<wz)return; if (begin==end) { p[root].Max=max(p[root].Max,z); return; } int mid=(begin+end)/2; if (wz<=mid) { if (!p[root].ls)p[root].ls=++cnt; gengxin(p[root].ls,begin,mid,wz,z); } else { if (!p[root].rs)p[root].rs=++cnt; gengxin(p[root].rs,mid+1,end,wz,z); } p[root].Max=max(p[p[root].ls].Max,p[p[root].rs].Max); } int chaxun(int root,int begin,int end,int begin2,int end2){ if (!root || begin>end2 || end<begin2)return 0; if (begin>=begin2 && end<=end2)return p[root].Max; int mid=(begin+end)/2; return max(chaxun(p[root].ls,begin,mid,begin2,end2),chaxun(p[root].rs,mid+1,end,begin2,end2)); } int getans(int x,int y){ int ans=0; for (;x>=1;x-=(x&-x))ans=max(ans,chaxun(rt[x],1,INF,1,y)); return ans; } void upd(int x,int y,int z){ for (;x<=INF;x+=(x&-x)) { if (!rt[x])rt[x]=++cnt; gengxin(rt[x],1,INF,y,z); } } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { scanf("%d",&a[i]); Max[i]=Min[i]=a[i]; } for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); Max[x]=max(Max[x],y); Min[x]=min(Min[x],y); } for (int i=1;i<=n;i++) { f[i]=getans(Min[i],a[i])+1; upd(a[i],Max[i],f[i]); ans=max(ans,f[i]); } printf("%d ",ans); return 0; }