您的位置:首页 > 其它

bzoj2253 [2010 Beijing wc]纸箱堆叠(CDQ+dp)

2017-12-18 17:13 411 查看

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。

2<=P<=2000000000,1<=a<=p-1,a^k modp<>0,ap<=2000000000,1<=N<=50000
[Submit][Status][Discuss]



分析:

这道题第一眼就是dp

状态:f[i]表示以第i个盒子为最大盒,能装的最多盒子

转移方程:f[i]=max(f[j]+1) (j盒能装进i中)

现在的问题就是针对每一个i,寻找符合条件j

一看这个形式:三维偏序CDQ!?

但是这个p的范围是2*1e9,直接开树状数组就是找死

那怎么办呢?离散化啊!

但是CDQ只能计算出针对第i个盒子,有多少个盒子能够放在ta里面,但是我们需要找到具体是哪个啊,怎么办

仔细观察一下转移方程:

f[i]=max(f[j]+1)

实际上我们只关心最大值

而我们的树状数组是可以维护最大值的

这样我们的解题思路就出来了:

计算出每一个盒子的尺寸(注意三维的数据要排好序)

把所有数据离散化

CDQ分治(树状数组维护前驱最大值)、

我好不容易把做法想全huo了,然而写出来就是WA

看了一下题解,发现有几个蛮难受的点

离散化(这样比较优美)

sort(tmp+1,tmp+1+n*3);
for (int i=1;i<=n;i++) po[i].a=lower_bound(tmp+1,tmp+1+n*3,po[i].a)-tmp;
for (int i=1;i<=n;i++) po[i].b=lower_bound(tmp+1,tmp+1+n*3,po[i].b)-tmp;
for (int i=1;i<=n;i++) po[i].c=lower_bound(tmp+1,tmp+1+n*3,po[i].c)-tmp;


变量x

(具体作用没有搞明白,大概是为了防止WA)

int M=(L+R)>>1,x=1e9;
for (int i=L;i<R;i++)
if (po[i].a!=po[i+1].a && abs(i-M)<abs(x-M)) x=i;
if (x==1e9) return;
M=x;


CDQ的顺序

这道题目和以前做的CDQ分治不一样,

这道题前面的信息对后面是有影响的

也就是说,只有[L,M]对[M+1,R]的影响处理完了,才能做[M+1,R]的

这样的话处理顺序是先[L,M],再处理区间之间的影响,最后[M+1,R]

CDQ(L,M);
sort(po+L,po+M+1,cmp);    //按b排序
sort(po+M+1,po+R+1,cmp);
...
sort(po+M+1,po+R+1,cmp0);  //一定要恢复之前的状态(按a排序),再分治右区间
CDQ(M+1,R);


tip

mdzz,这道题磨了一下午。。。

//这里写代码片
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long

using namespace std;

const int N=50010;
struct node{
int a,b,c,ans;
};
node po
,q
;
int t[N<<2],n,p,a,tmp[N<<2];

void clear(int x) {for (int i=x;i<=n*3;i+=(i&(-i))) t[i]=0;}
void add(int x,int z) {for (int i=x;i<=n*3;i+=(i&(-i))) t[i]=max(t[i],z);}
int ask(int x) {int ans=0;for (int i=x;i>0;i-=(i&(-i))) ans=max(ans,t[i]);return ans;}

int cmp0(const node &A,const node &B)
{
if (A.a!=B.a) return A.a<B.a;
else if (A.b!=B.b) return A.b<B.b;
else return A.c<B.c;
}

int cmp(const node &A,const node &B)
{
if (A.b!=B.b) return A.b<B.b;
else return A.c<B.c;
}

void CDQ(int L,int R)
{
if (L==R) return;
int M=(L+R)>>1,x=1e9; for (int i=L;i<R;i++) if (po[i].a!=po[i+1].a && abs(i-M)<abs(x-M)) x=i; if (x==1e9) return; M=x;

CDQ(L,M);
sort(po+L,po+M+1,cmp); //按b排序
sort(po+M+1,po+R+1,cmp);

int t1=L,t2=M+1;
while (t2<=R)
{
if ((t1<=M&&po[t1].b<po[t2].b)||t2>R) //严格小于
add(po[t1].c,po[t1].ans),t1++;
else
po[t2].ans=max(po[t2].ans,ask(po[t2].c-1)+1),t2++; //严格小于
}
for (int i=L;i<t1;i++)
clear(po[i].c);

sort(po+M+1,po+R+1,cmp0); //一定要恢复之前的状态(按a排序),再分治右区间
CDQ(M+1,R);
}

int main()
{
scanf("%d%d%d",&a,&p,&n);
int num=a%p;
for (int i=1;i<=n;i++)
{
po[i].ans=1;
po[i].a=num%p; tmp[i]=num%p; num=num*a%p;
po[i].b=num%p; tmp[i+n]=num%p; num=num*a%p;
po[i].c=num%p; tmp[i+n*2]=num%p; num=num*a%p;
if (po[i].a<po[i].b) swap(po[i].a,po[i].b);
if (po[i].b<po[i].c) swap(po[i].b,po[i].c);
if (po[i].a<po[i].b) swap(po[i].a,po[i].b);
}

//离散化
sort(tmp+1,tmp+1+n*3); for (int i=1;i<=n;i++) po[i].a=lower_bound(tmp+1,tmp+1+n*3,po[i].a)-tmp; for (int i=1;i<=n;i++) po[i].b=lower_bound(tmp+1,tmp+1+n*3,po[i].b)-tmp; for (int i=1;i<=n;i++) po[i].c=lower_bound(tmp+1,tmp+1+n*3,po[i].c)-tmp;

sort(po+1,po+1+n,cmp0); //按照a排序

CDQ(1,n);

int Ans=-1;
for (int i=1;i<=n;i++) Ans=max(Ans,po[i].ans);
printf("%d",Ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: