【BZOJ2253】纸箱堆叠 [CDQ分治]

时间:2024-07-11 11:07:44

纸箱堆叠

Time Limit: 30 Sec  Memory Limit: 256 MB
[Submit][Status][Discuss]

Description

  P 工厂是一个生产纸箱的工厂。
  纸箱生产线在人工输入三个参数 n p a , 之后即可自动化生产三边边长为

  (a mod P,a^2 mod p,a^3 mod P)
  (a^4 mod p,a^5 mod p,a^6 mod P)
  ....
  (a^(3n-2) mod p,a^(3n-1) mod p,a^(3n) mod p)

  的n个纸箱。
  在运输这些纸箱时,为了节约空间,必须将它们嵌套堆叠起来。
  一个纸箱可以嵌套堆叠进另一个纸箱当且仅当它的最短边、次短边和最长边长度分别严格小于另一个纸箱的最短边、次短边和最长边长度。
  你的任务是找出这n个纸箱中数量最多的一个子集,使得它们两两之间都可嵌套堆叠起来。

Input

  输入文件的第一行三个整数,分别代表 a,p,n

Output

  输出文件仅包含一个整数,代表数量最多的可嵌套堆叠起来的纸箱的个数。

Sample Input

  10 17 4

Sample Output

  2
【样例说明】
  生产出的纸箱的三边长为(10, 15, 14), (4, 6, 9) , (5, 16, 7), (2, 3, 13)。
  其中只有(4, 6, 9)可堆叠进(5, 16, 7),故答案为 2。

HINT

  2<=P<=2000000000,  1<=a<=p-1, a^k mod p<>0, ap<=2000000000, 1<=N<=50000

Main idea

  每一个元素有三个属性a,b,c,求出最大可连续堆叠个数(可堆叠条件是a1<a2,b1<b2,c1<c2)

Solution

  题目显然是三维偏序问题,运用CDQ分治求解。

  用排序处理a保证a有序,分治的时候满足左区间的b都小于右区间的b,再处理c,这样问题就转化为了求一个点在一个平面上横纵坐标都小于它的点有几个,用树状数组处理即可。

  发现这样处理之后答案只能满足<=该点,考虑如何令答案严格小于

  首先b,c的严格小于处理显然,因为a是sort保证的那么如何要使得a的统计严格小于呢?只需要在b的sort前将分割的指针向左移动到第一个不等于的即可,结合分治考虑一下while(q[mid].a==q[mid-1].a) mid--,发现这样处理最后会影响到排序,所以做右区间的时候重新按照a排序一下即可。

  考虑如何统计答案,发现显然有: q[j].ans=max(q[j].ans,Query(q[j].c-1)+1)

Code

 #include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std; const int ONE=; int n,MOD,a,m;
int PD[];
int res;
int C[ONE];
int Ans,cnt; struct power
{
int a,b,c;
int ans;
}q[ONE]; struct point
{
int pos,value;
}Lisa[ONE]; int get()
{
int res,Q=; char c;
while( (c=getchar())< || c>)
if(c=='-')Q=-;
if(Q) res=c-;
while((c=getchar())>= && c<=)
res=res*+c-;
return res*Q;
} int cmp(const power &a,const power &b)
{
if(a.a!=b.a) return a.a<b.a;
if(a.b!=b.b) return a.b<b.b;
return a.c<b.c;
} int cdp(const power &a,const power &b)
{
if(a.b!=b.b) return a.b<b.b;
return a.c<b.c;
} int rule(const power &a,const power &b)
{
return (a.a==b.a && a.b==b.b && a.c==b.c);
} int lowbit(int x)
{
return x&-x;
} int Add(int R,int x)
{
for(int i=R;i<=cnt;i+=lowbit(i))
C[i]=max(C[i],x);
} int Query(int R)
{
res=;
for(int i=R;i>=;i-=lowbit(i))
res=max(res,C[i]);
return res;
} int Clear(int R)
{
for(int i=R;i<=cnt;i+=lowbit(i))
C[i]=;
} int clis(const point &a,const point &b)
{
return a.value<b.value;
} void GetLisan()
{
sort(q+,q+n+,cmp);
n=unique(q+,q+n+,rule)--q; for(int i=;i<=n;i++)
{
Lisa[i].pos=i;
Lisa[i].value=q[i].c;
}
sort(Lisa+,Lisa+n+,clis); cnt=;
Lisa[].value=-;
for(int i=;i<=n;i++)
{
if(Lisa[i].value!=Lisa[i-].value) cnt++;
q[Lisa[i].pos].c=cnt;
} } void Deal(int l,int r)
{
if(l>=r) return;
int mid=(l+r)/;
while(q[mid].a==q[mid-].a) mid--;
if(mid<l) return;
Deal(l,mid);
sort(q+l,q+mid+,cdp); sort(q+mid+,q+r+,cdp); int i=l,j=mid+;
while(j<=r)
{
while(i<=mid && q[i].b<q[j].b)
{
Add(q[i].c,q[i].ans);
i++;
} q[j].ans=max(q[j].ans,Query(q[j].c-)+);
j++;
} for(int T=l;T<=mid;T++)
{
Clear(q[T].c);
}
sort(q+mid+,q+r+,cmp);
Deal(mid+,r);
} int main()
{
a=get(); MOD=get(); n=get();
int j=; res=;
for(int i=;i<=n;i++)
{
for(int j=;j<=;j++)
{
res=(long long)res*a%MOD;
PD[j]=res;
}
sort(PD+,PD++);
q[i].a=PD[]; q[i].b=PD[]; q[i].c=PD[];
q[i].ans=;
} GetLisan(); Deal(,n);
for(int i=;i<=n;i++)
Ans=max(Ans,q[i].ans); printf("%d",Ans);
}