题意:
给出一个二元组(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;
}