hdu 4521 小明系列问题——小明序列(单点更新)

题意:有多组测试数据,每组数据的n和d表示,有n个数,求间距大于d的最长上升序列。(1<=n<=10^5 , 0<=d<=10^5)

        如果数据范围比较小,就是一比较水的DP了。但是10^5级别的数据,O(n^2)的复杂度显然不科学。
        用线段树搞定。线段树中叶子结点表示值为i的并且以其结束的最长上升序列是多少。每次转移的时候,查询比当前值小的最大值就可以了。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=1e5+5;
#define LL(x) (x<<1)
#define RR(x) (x<<1|1)
#define MID(a,b) (a+((b-a)>>1))
int a[N],imx[N];
struct Segtree
{
	struct node
	{
		int lft,rht,mx;
		int mid(){return MID(lft,rht);}
	}tree[N*4];
	void build(int lft,int rht,int ind)
	{
		tree[ind].lft=lft;	tree[ind].rht=rht;
		tree[ind].mx=0;
		if(lft!=rht)
		{
			int mid=tree[ind].mid();
			build(lft,mid,LL(ind));
			build(mid+1,rht,RR(ind));
		}
	}
	void updata(int pos,int ind,int valu)
	{
		if(tree[ind].lft==tree[ind].rht) tree[ind].mx=max(tree[ind].mx,valu);
		else
		{
			int mid=tree[ind].mid();
			if(pos<=mid) updata(pos,LL(ind),valu);
			if(pos> mid) updata(pos,RR(ind),valu);
			tree[ind].mx=max(tree[LL(ind)].mx,tree[RR(ind)].mx);
		}
	}
	int query(int st,int ed,int ind)
	{
		int lft=tree[ind].lft,rht=tree[ind].rht;
		if(st<=lft&&rht<=ed) return tree[ind].mx;
		else
		{
			int mid=tree[ind].mid();
			int mx1=0,mx2=0;
			if(st<=mid) mx1=query(st,ed,LL(ind));
			if(ed> mid) mx2=query(st,ed,RR(ind));
			return max(mx1,mx2);
		}
	}
}seg;
int main()
{
	int n,d;
	while(scanf("%d%d",&n,&d)!=EOF)
	{
		int ans=0,ed=0;
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			ed=max(ed,a[i]);
		}

		seg.build(0,ed,1);
		for(int i=0;i<n;i++)
		{
			if(i-d-1>=0) seg.updata(a[i-d-1],1,imx[i-d-1]);
			if(a[i]>0) imx[i]=seg.query(0,a[i]-1,1)+1;
			else imx[i]=1;
			ans=max(ans,imx[i]);
		}
		printf("%d\n",ans);
	}
	return 0;
}


你可能感兴趣的:(hdu 4521 小明系列问题——小明序列(单点更新))