jdfz-2764 二维LIS

题意:

题目链接:oj.jdfz.com.cn:8081/oldoj/problem.php?id=2764

给出一个二元组(xi,yi)的序列,定义a[i]小于a[j]为xi<xj且y[i]<y[j];

求LIS的长度;

n,x,y<=20000;


题解:

设f[i]为以i为结尾的LIS长度;

考虑硬搞,对每个i来找x,y都严格小于它的最大f值;

也就是在坐标系上查询矩形的最大值;

单点插入,矩形查询;

显然线段树套线段树可以搞,复杂度O(nlog^2n);

但是这东西的空间复杂度还是颇大的,而且代码实现也有难度;

所以考虑分治;

题目中的二维这个条件给我们造成了困扰,所以我们可以按其中一维x排序;

然后上CDQ上CDQ分治;

对于一段[l,r]区间,先由时间序也就是原序列中的下标分成两个区间,在此过程中也保持两个序列的x有序;

然后这时左区间是子问题,递归解决(边界l==r退出);

递归结束后,用左区间来更新右区间;

更新的时候我是把左区间中点插入线段树,然后查最大值(这一步带了个log);

之后就是递归处理右区间,最后归并一下就好了;

每层递归O(Len*logn),共logn层;

时间复杂度O(nlog^2n),空间复杂度O(n);

这样CDQ分治的优越性也是显现出来了,分治子问题的空间一直是线性的;

注意维护f数组是维护那个i的原下标的,而不是目前对x排序的下标;

还有一点就是每层的线段树是不能清的,因为那样每层的复杂度就不是O(Len*logn)而是O(nlogn)了;

时间复杂度会上升到大概O(n^2)的级别;

但是由于是20000的数据O(n^2)卡卡也就过了;


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 22000
#define lson l,mid,no<<1
#define rson mid+1,r,no<<1|1
using namespace std;
struct node
{
	int x,y,no;
}a[N],temp[N];
const int T=20000;
int f[N],ma[N<<2],t[N<<2],tot;
int cmp(node a,node b)
{
	return a.x<b.x;
}
void Pushup(int no)
{
	if(t[no<<1]==tot&&t[no<<1|1]==tot)
		ma[no]=max(ma[no<<1],ma[no<<1|1]),
		t[no]=tot;
	else
	{
		if(t[no<<1]==tot)
			ma[no]=ma[no<<1],t[no]=tot;
		if(t[no<<1|1]==tot)
			ma[no]=ma[no<<1|1],t[no]=tot;
	}
}
void update(int l,int r,int no,int k,int val)
{
	if(l==r)
	{
		if(t[no]==tot)
			ma[no]=max(ma[no],val);
		else
			ma[no]=val,t[no]=tot;
	}
	else
	{
		int mid=(l+r)>>1;
		if(k<=mid)	update(lson,k,val);
		else		update(rson,k,val);
		Pushup(no);
	}
}
int query(int l,int r,int no,int st,int en)
{
	if(st<=l&&r<=en)
	{
		if(t[no]==tot)	return ma[no];
		else			return 0;
	}
	else
	{
		int mid=(l+r)>>1;
		if(en<=mid)			return query(lson,st,en);
		else if(st>mid)		return query(rson,st,en);
		else	return max(query(lson,st,en),query(rson,st,en));
	}
}
void merge(int l,int r)
{
	int i,j,k,mid=(l+r)>>1;
	memcpy(temp+l,a+l,sizeof(node)*(r-l+1));
	for(i=l,j=mid+1,k=l;k<=r;k++)
	{
		if(i<=mid&&j<=r)
			a[k]=temp[i].x<temp[j].x?temp[i++]:temp[j++];
		else
			a[k]=(j==r+1)?temp[i++]:temp[j++];
	}
}
void slove(int l,int r)
{
	if(l==r)	return ;
	int mid=(l+r)>>1,i,j,k;
	memcpy(temp+l,a+l,sizeof(node)*(r-l+1));
	for(i=l,j=l,k=mid+1;i<=r;i++)
	{
		if(temp[i].no<=mid)	a[j++]=temp[i];
		else				a[k++]=temp[i];
	}
	slove(l,mid);
	tot++;
	for(i=mid+1,j=l;i<=r;i++)
	{
		while(a[j].x<a[i].x&&j<=mid)
		{
			update(1,T,1,a[j].y,f[a[j].no]);
			j++;
		}
		if(a[i].y!=1)
			f[a[i].no]=max(f[a[i].no],query(1,T,1,1,a[i].y-1)+1);
		else
			f[a[i].no]=max(f[a[i].no],1);
	}
	slove(mid+1,r);
	merge(l,r);
}
int main()
{
	int n,i,j,k,ans;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		scanf("%d%d",&a[i].x,&a[i].y),
		a[i].no=i;
	sort(a+1,a+n+1,cmp);
	f[1]=1;
	slove(1,n);
	for(i=1,ans=0;i<=n;i++)
		ans=max(ans,f[i]);
	printf("%d",ans);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值