来看这样一道题
(bzoj陌上百合)
描述:
给出 n 个点(x,y,z),请找出最长上升子序列,即对于选择序列中的 i
输出最长上升子序列的长度和方案数。
输入:
第一行包含一个整数 n
接下来 n 行,每行有 3 个整数 xi,yi,zi
输出:
输出长度和方案数(方案数对2^30取mod)
输入输出样例:
cdq.in cdq.out
3
2 0 0
0 1 1
0 1 0
2 1
5
3 0 0
0 1 0
0 0 1
0 2 2
3 3 3
3 2
数据范围:
x,y,z<=1000000000
对于 20%数据 n<=1000
对于 100%数据 n<=100000
这就是经典的3维偏序.
三维偏序是可以对点排序之后求的
首先看二维偏序:(x,y)
如果我们对x排序,那么就转化成求y的lis
同理,我们也需要一些转化.
按照cdq分治基本思想,一维排序,二维分治,三维树状数组(数据结构)
-----------------------------------------------------------------------------------------------
接下来就以这道题为例,浅谈cdq分治思想
假设我们按第一维的坐标排好序了,我们就需要在x排好序的点对(y,z)中找二维偏序.
因为之前已经排过序(x)了,所以直接再排序是有一点问题的,在这里可以写树套树维护.
树套树的码量有点大,而且不太好写,所以就不谈了。
谈点实际的:
我们已经对x这一维排好序了,那么我们将[l,r]的点对分成两个区间[l,mid] 和 [mid+1,r]
然后依次递归调用,先处理[l,mid]的信息,然后处理[l,mid]对[mid+1,r]的影响,最后处理[mid+1,r]的信息
(为什么要这样?因为这道题是求三维lis,左边的区间会对右边的产生影响.)
继续刚才的分治:如果我们得到了两个区间的信息,就可以对第3维维护信息了。
可能有些不懂,下面讲一下具体的吧:
(下面是计算[l,mid]对[mid+1,r]的影响)
我们将2分的区间分别按y排序(因为第一维之前排过序,可以保证[mid+1,r]的所有x>=[l,mid]的x. 因为是考虑左区间对右区间的影响,所以就不用考虑区间内部信息)
而后第二位也是有序了的。
然后枚举[mid+1,r]每个元素,并且维护左区间的一个指针i,一旦当前枚举到的元素j的y,就把左区间所有i.y<=j.y的信息更新到树状数组里面去(我们保证了y的单调性,所以复杂度大致是 区间长*更新单次的时间)
树状数组用来干什么?我们枚举到右区间一个j,将左区间中所有i.y<=j.y(第一次排序保证了i.x<=j.x)的z在树状数组对应的位置的dp值更新为max(dp[i]). 所以树状数组是用来维护第3维的dp值
对于枚举到的j,更新了所有i.y<=j.y的i的信息之后dp[j]=query([1,j.z])(这一段区间的最大dp值)+1;
然后把所有dp值求max就得到了答案1
那么怎么求方案呢?
我其实也很迷茫,不过参考了各种资料后,发现只需要再维护一个数组保存方案数即可.
在更新dp值的时候,if(dp[j]
if(dp[j]==getmax(1,j.z).dp)cnt[j]+=getmax(1,j.z).cnt;
就行了!
有不清楚的,再细想一下吧!
AC代码:
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
const LL M=1<<30;
using namespace std;
double st,ft;
const int maxn=100000+20;
struct xnode
{
int x,y,z,id;
}q[maxn],tl[maxn],tr[maxn];
int dp[maxn];
LL c[maxn];
int n;
bool cmpx(xnode a,xnode b)
{
if(a.x!=b.x)return a.x>1;
int t1,t2;
t1=t2=0;
for(int i=l;i<=mid;i++)tl[++t1]=q[i];
for(int i=mid+1;i<=r;i++)tr[++t2]=q[i];
sort(tl+1,tl+t1+1,cmpy);
sort(tr+1,tr+t2+1,cmpy);
for(int i=1,j=1;j<=t2;j++)
{
while(tl[i].y<=tr[j].y&&i<=t1)
{
updata(tl[i].z,dp[tl[i].id],c[tl[i].id]);
i++;
}
Ans sum=query(tr[j].z);
sum.dp++;
if(dp[tr[j].id]>1;
cal(l,mid);
work(l,r);
cal(mid+1,r);
}
int main()
{
freopen("cdq.in","r",stdin);
freopen("cdq.out","w",stdout);
cnt=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
q[i].id=i;
dp[i]=1;
c[i]=1;
}
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].z);
det[++cnt]=q[i].z;
}
sort(det+1,det+cnt+1);
m=unique(det+1,det+cnt+1)-det-1;
for(int i=1;i<=n;i++)q[i].z=find(q[i].z);
sort(q+1,q+n+1,cmpx);
cal(1,n);
int ans=0;
LL sum=0;
for(int i=1;i<=n;i++)
{
if(dp[i]>ans)
{
ans=dp[i];
sum=c[i]%M;
}
else if(dp[i]==ans)
{
sum+=c[i];
sum%=M;
}
}
printf("%d %I64d\n",ans,sum);
return 0;
}