【BZOJ2090/2089】[Poi2010]Monotonicity 2 动态规划+线段树

【BZOJ2090/2089】[Poi2010]Monotonicity

Description

给出N个正整数a[1..N],再给出K个关系符号(>、<或=)s[1..k]。
选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1]。
求出L的最大值。

Input

第一行两个正整数,分别表示N和K (N, K <= 500,000)。
第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6)。
第三行给出K个空格隔开关系符号(>、<或=),第i个表示s[i]。

Output

一个正整数,表示L的最大值。

Sample Input

7 3
2 4 3 1 3 5 3
< > =

Sample Output

6

题解:一开始没看到不要求连续,以为是一道KMP的裸题。。。

我们令f[i]表示第i个数最多能成为子序列的第f[i]项,所以当我们确定f[i]后,i的下一项和i的关系是确定的,所以我们考虑如何实现查询

我们根据三种关系,维护2个权值线段树和一个数组,分别对应>,<,=。=我就不说了,我们以<举例

如果f[i]对应的符号是<,那么我们将它放到<所对应的权值线段树中,权值线段树上v[i]位置的值就是f[i],然后在枚举到j的时候,就查询[1,v[j]-1]上f[]的最大值,再+1就能更新f[j]

#include <cstdio>
#include <iostream>
#include <cstring>
#define lson x<<1
#define rson x<<1|1
using namespace std;
int t[500010],v[500010],f[500010];
char str[5];
int n,m,nm,ans;
struct SAG
{
	int sm[4000010];
	void pushup(int x)
	{
		sm[x]=max(sm[lson],sm[rson]);
	}
	void updata(int l,int r,int x,int p,int v)
	{
		if(l==r)
		{
			sm[x]=v;
			return ;
		}
		int mid=l+r>>1;
		if(p<=mid)	updata(l,mid,lson,p,v);
		else	updata(mid+1,r,rson,p,v);
		pushup(x);
	}
	int query(int l,int r,int x,int a,int b)
	{
		if(a>b)	return 0;
		if(a<=l&&r<=b)	return sm[x];
		int mid=l+r>>1;
		if(b<=mid)	return query(l,mid,lson,a,b);
		if(a>mid)	return query(mid+1,r,rson,a,b);
		return max(query(l,mid,lson,a,b),query(mid+1,r,rson,a,b));
	}
}s[2];
int s2[1000010];
int max(int a,int b,int c)
{
	return max(max(a,b),c);
}
int main()
{
	scanf("%d%d",&n,&m);
	int i,j;
	for(i=1;i<=n;i++)	scanf("%d",&v[i]),nm=max(v[i],nm);
	for(i=1;i<=m;i++)
	{
		scanf("%s",str);
		if(str[0]=='>')	t[i]=0;
		if(str[0]=='<')	t[i]=1;
		if(str[0]=='=')	t[i]=2;
	}
	for(i=1;i<=n;i++)
	{
		f[i]=max(s[0].query(1,nm,1,v[i]+1,nm),s[1].query(1,nm,1,1,v[i]-1),s2[v[i]])+1;
		ans=max(ans,f[i]);
		j=t[(f[i]-1)%m+1];
		if(j==2)	s2[v[i]]=max(f[i],s2[v[i]]);
		else	s[j].updata(1,nm,1,v[i],f[i]);
	}
	printf("%d",ans);
	return 0;
}
posted @ 2017-05-15 20:44  CQzhangyu  阅读(336)  评论(0编辑  收藏  举报