[BZOJ 2253][2010 Beijing wc]纸箱堆叠:CDQ分治|DP

本文探讨了一种涉及三维偏序的最长上升子序列问题,采用CDQ分治策略进行求解。通过按不同维度排序并利用树状数组维护状态,有效解决了三维偏序条件下的最优化问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击这里查看原题

首先这是一个严格三维偏序问题,可以用CDQ分治来做,其次这又是一个三维的最长上升子序列问题,dp[i]表示以第i个箱子为结尾的最长长度。
于是我们先将所有箱子按x大小排序,因为要严格上升,因此对于x值相等的情况我们要微调mid的位置,确保x值相等的箱子在一个分治区间内。在每个分治区间内又按y大小排序,对左区间用树状数组维护前缀最大值,对右区间查询前缀最大值。

/*
User:Small
Language:C++
Problem No.:2253
*/
#include<bits/stdc++.h>
#define ll long long
#define inf 999999999
using namespace std;
const int M=1e5+5;
int a,mod,n,ans,has[M],cnt,f[M];
struct no{
    int a,b,c,ans;
}p[M];
struct BIT{
    int c[M];
    BIT(){
        memset(c,0,sizeof(c));
    }
    void add(int x,int k){
        for(;x<=n;x+=x&-x){
            c[x]=max(c[x],k);
        }
    }
    int query(int x){
        int res=0;
        for(;x;x-=x&-x)
            res=max(res,c[x]);
        return res;
    }
    void clear(int x){
        for(;x<=n;x+=x&-x)
            c[x]=0;
    }
}T;
bool cmpa(no a,no b){
    return a.a==b.a?(a.b==b.b?a.c<b.c:a.b<b.b):a.a<b.a;
}
bool cmpy(no a,no b){
    return a.b==b.b?a.c<b.c:a.b<b.b;
}
void solve(int l,int r){
    if(l>=r) return;
    int mid=-1,l1=(l+r)>>1,l2=l1+1;
    while(l1>=l||l2<=r){
        if(l1>=l&&p[l1].a!=p[l1+1].a){
            mid=l1;
            break;
        }
        if(l2<=r&&p[l2-1].a!=p[l2].a){
            mid=l2-1;
            break;
        }
        l1--;
        l2++;
    }
    if(mid==-1) return;
    solve(l,mid);
    sort(p+l,p+mid+1,cmpy);
    sort(p+mid+1,p+r+1,cmpy);
    for(int i=mid+1,j=l;i<=r;i++){
        for(;j<=mid&&p[j].b<p[i].b;j++)
            T.add(p[j].c,p[j].ans);
        p[i].ans=max(p[i].ans,T.query(p[i].c-1)+1);
    }
    for(int i=l;i<=mid;i++) T.clear(p[i].c);
    sort(p+mid+1,p+r+1,cmpa);
    solve(mid+1,r);
}
int main(){
    freopen("data.in","r",stdin);//
    scanf("%d%d%d",&a,&mod,&n);
    int tmp=1; 
    for(int i=1;i<=n;i++){
        p[i].a=tmp=(ll)tmp*a%mod;
        p[i].b=tmp=(ll)tmp*a%mod;
        p[i].c=tmp=(ll)tmp*a%mod;
        p[i].ans=1;
        if(p[i].a>p[i].b) swap(p[i].a,p[i].b);
        if(p[i].a>p[i].c) swap(p[i].a,p[i].c);
        if(p[i].b>p[i].c) swap(p[i].b,p[i].c);
        has[++cnt]=p[i].c;
    }
    sort(p+1,p+n+1,cmpa);
    sort(has+1,has+cnt+1);
    for(int i=1;i<=n;i++)
        p[i].c=lower_bound(has+1,has+cnt+1,p[i].c)-has;
    solve(1,n);
    ans=1;
    for(int i=1;i<=n;i++)
        ans=max(ans,p[i].ans);
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值