HEOI2016 序列

题意:

最长上升子序列,不过是永久最长上升子序列而且每次变化只能改一个值,也就是说对于 i 和 j,i 处所有值都要小于等于 j 处,j 处所有值都要大于等于 i 处才可以。

50pts 做法

根据题意,我们易得基础 DP:

$$f_i=\max(f_i,f_j+1)$$

条件:

  1. $maxx_j \leq a_i$
  2. $minn_i \leq a_j$
  3. $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 转移的条件:

  1. $maxn_i\le a_i$
  2. $a_j \le minn_i$
  3. $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();
}

上一篇:2021-11-23最适合入门的Shiro框架教程(二)(springboot整合Shiro,JdbcRealm表规范,IniRealm,Shiro常用标签,权限菜单实现)


下一篇:CF1529-A. Eshag Loves Big Arrays