bzoj 2253: [2010 Beijing wc]纸箱堆叠 (CDQ分治+DP)

题目描述

传送门

题目大意:每个物品有三个参数(x,y,z),一个物品只有三个参数同时严格小于另一个物品,才能放到另一个物品中。问最多多少个两两嵌套。

题解

我们先按照x坐标排好序,然后进行CDQ分治。
分治的时候先计算[l,mid]的中点的答案。
然后按照y排序,将原本[l,mid]中的点一次加入他们z值对应的树状数组中。
[r,mid]中的点从树状数组中查询小于z的区间最小值,更新答案。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 50003
#define LL long long 
using namespace std;
int f[N*3],n,base,p,tr[N*3],mul[N*3],b[N*3],cnt,ll[N*3],rr[N*3],c[N*3];
bool mark[N];
struct data{
    int x,y,z,id,pd;
}a[N];
int cmp(data a,data b)
{
    return a.x<b.x;
}
int cmp1(data a,data b)
{
    return  a.y<b.y||a.y==b.y&&a.x<b.x;
}
int lowbit(int x){
    return x&(-x);
}
void change(int x,int val)
{
    for (int i=x;i<=cnt;i+=lowbit(i))
     tr[i]=max(tr[i],val); 
}
int query(int x)
{
    int ans=0;
    if (!x) return 0;
    for (int i=x;i>=1;i-=lowbit(i))
     ans=max(ans,tr[i]);
    return ans;
}
void clear(int x)
{
    for (int i=x;i<=cnt;i+=lowbit(i)) tr[i]=0;
}
void divide(int l,int r)
{
    if (l>=r) return;
    sort(a+l,a+r+1,cmp);
    //cout<<l<<" "<<r<<endl;
    //for (int i=l;i<=r;i++) cout<<a[i].x<<" "<<a[i].y<<" "<<a[i].z<<" "<<a[i].id<<endl; 
    int mid=(l+r)/2;
    int L=ll[l]; int R=rr[r];
    divide(l,mid);
    for (int i=L;i<=rr[mid];i++) a[i].pd=1;
    for (int i=rr[mid]+1;i<=R;i++) a[i].pd=0;
    sort(a+L,a+R+1,cmp1); int j;
    for (int i=L;i<=R;i=j+1) {
     j=i;
     while (j<R&&a[i].y==a[j+1].y) j++;
     for (int k=i;k<=j;k++) 
      if (a[k].pd==0) {
        int t=a[k].id;
        f[t]=max(f[t],query(a[k].z-1)+1);
      }
     for (int k=i;k<=j;k++) 
      if (a[k].pd==1) change(a[k].z,f[a[k].id]);
    }
    for (int i=L;i<=R;i++) 
     if (a[i].pd==1) clear(a[i].z);
    sort(a+L,a+R+1,cmp);
    divide(mid+1,r);
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d%d%d",&base,&p,&n);
    mul[0]=1; int t=0;
    for (int i=1;i<=3*n;i++) mul[i]=(LL)mul[i-1]*base%p,b[i]=mul[i];
    sort(b+1,b+3*n+1);
    cnt=unique(b+1,b+3*n+1)-b-1;
    for (int i=1;i<=n;i++){
        int t1=(i-1)*3; a[i].id=i;
        //cout<<mul[t+1]<<" "<<mul[t+2]<<" "<<mul[t+3]<<endl;
        a[i].x=lower_bound(b+1,b+cnt+1,mul[t1+1])-b; 
        a[i].y=lower_bound(b+1,b+cnt+1,mul[t1+2])-b; 
        a[i].z=lower_bound(b+1,b+cnt+1,mul[t1+3])-b;
        if (a[i].y<a[i].x) swap(a[i].x,a[i].y);
        if (a[i].z<a[i].x) swap(a[i].x,a[i].z);
        if (a[i].z<a[i].y) swap(a[i].z,a[i].y); 
        //cout<<a[i].x<<" "<<a[i].y<<" "<<a[i].z<<endl;
        c[++t]=a[i].x;
    }
    sort(c+1,c+t+1);
    t=unique(c+1,c+t+1)-c-1;
   // cout<<t<<endl;
    for (int i=1;i<=n;i++) a[i].x=lower_bound(c+1,c+t+1,a[i].x)-c;
    for (int i=1;i<=n;i++)  f[i]=1;
    sort(a+1,a+n+1,cmp);
    a[n+1].x=1000000000;
    for (int i=1;i<=n;i++) {
        //cout<<a[i].x<<endl;
        if (a[i-1].x<a[i].x) ll[a[i].x]=i;
        if (a[i+1].x>a[i].x) rr[a[i].x]=i;
    }
    divide(1,t);
    int ans=0;
    for (int i=1;i<=n;i++) ans=max(ans,f[i]);
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值