HDU 5324 分治

很好的题,看完题意原本以为十几分钟就能AC,结果WA了一整天,修改重交十几次终于AC,通过此题,加深了对分治算法的理解和码力。


题意很简单,一个三维偏序的变形。
但差别在于x坐标是已经排列好且去重的数据给出顺序。
要你求满足Y非严递减和Z非严递增的最长序列。

易掉的坑:(其实是我掉的坑qwq)

  1. 直接仿造HDU 5618的做法是不对的,因为此题要求最长序列任意两两之间均满足约束关系。

  2. 数据范围很大,肯定是需要离散化的,而且离散化是一定要离散到1n的区间,而不是0n,否则后面的树状数组的使用可能会出问题。

  3. 关于输出:
    只输出长度是很简单的,难就难在输出字典序最小的情况,我一开始是按Y坐标进行排序,Z坐标插入树状数组来得到每一个点的最优值(即以该点作为结尾点且满足约束条件的最长序列长度)
    然后此时需要从后往前找到路径,有两种方式可以选择:
    1.每次状态转移(即该点的最优值答案发生错误时)记录前一个点。
    2.每一个点的最优解改成以该点作为起始点且满足约束条件的最长序列的长度。

然后我选择了第二种方式,所以只需要改成按Z坐标排序,然后Y坐标插入树状数组即可实现。

  1. 关于状态转移:
    通过分治得到了左右区间,此时我们需要考虑右区间对左区间的影响,即对于左区间的P点,若右区间的Q点满足约束条件,则P点最优解(记作Ans[P])的状态转移方程为:

A n s [ P ] = m a x ( A n s [ P ] , A n s [ Q ] + 1 ) Ans[P] = max(Ans[P] , Ans[Q] + 1) Ans[P]=max(Ans[P],Ans[Q]+1)

而因为右区间可能同时存在多个满足条件的Q点,故我们需要借助树状数组得到最优解最大的Q点(细节见代码)
另外因为需要右区间的Q点最优解已知,故我们需要先处理右区间。

代码:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

const int A = 1e5 + 100;
class P{
public:
    int x,y,z;
    bool mark;
    P(int a=0,int b=0,int c=0,bool d=0):x(a),y(b),z(c),mark(d){}
}a[A],b[A];
int tem[A];
int n,Ans[A],Tree[A];

bool cmp(P &p,P &q){
    if(p.z != q.z) return p.z > q.z;
    if(p.y != q.y) return p.y < q.y;
    return p.x > q.x;
}

int lowbit(int x){
    return x & (-x);
}

void add(int pos,int val){
    while(pos < A){
        Tree[pos] = max(Tree[pos],val);
        pos += lowbit(pos);
    }
}

int Query(int pos){
    int res = 0;
    while(pos > 0){
        res = max(res,Tree[pos]);
        pos -= lowbit(pos);
    }
    return res;
}

void Clear(int pos){
    while(pos < A){
        Tree[pos] = 0;
        pos += lowbit(pos);
    }
}

void solve(int left,int right){
    if(left >= right) return;

    int mid = (left + right) >> 1;
    solve(mid+1,right);

    int tot = 0;
    for(int i=left ;i<=mid ;i++){
        b[++tot] = P(a[i].x,a[i].y,a[i].z,1);
    }
    for(int i=mid+1 ;i<=right;i++){
        b[++tot] = P(a[i].x,a[i].y,a[i].z,0);
    }
    sort(b+1,b+tot+1,cmp);

    /*for(int i=1 ;i<=tot ;i++){
       printf("b[%d].x = %d b[%d].y = %d b[%d].z = %d b[%d].mark = %d\n",i,b[i].x,i,b[i].y,i,b[i].z,i,b[i].mark);
    }*/

    for(int i=1 ;i<=tot ;i++){
        if(b[i].mark == 0){
            add(b[i].y,Ans[b[i].x]);
        }
        else{
            Ans[b[i].x] =  max(Ans[b[i].x],Query(b[i].y) + 1);
            //printf("Ans[%d] = %d\n",b[i].x,Ans[b[i].x]);
        }
    }
    for(int i=1 ;i<=tot ;i++){
        if(b[i].mark == 0){
            Clear(b[i].y);
        }
    }
    solve(left,mid);
}

int main(){
    while(~scanf("%d",&n)){
        int tot = 0;
        for(int i=1;i<=n ;i++){
            a[i].x = i;
            scanf("%d",&a[i].y);
            tem[tot++] = a[i].y;
        }
        for(int i=1 ;i<=n ;i++){
            scanf("%d",&a[i].z);
            tem[tot++] = a[i].z;
        }

        sort(tem,tem+tot);
        tot = unique(tem,tem+tot) - tem;
        for(int i=1 ;i<=n ;i++){
            a[i].y = lower_bound(tem,tem+tot,a[i].y) - tem + 1;
            a[i].z = lower_bound(tem,tem+tot,a[i].z) - tem + 1;
            //printf("a[%d].y = %d a[%d].z = %d\n",i,a[i].y,i,a[i].z);
        }

        for(int i=1 ;i<=n ;i++){
            Ans[i] = 1;
        }
        memset(Tree,0,sizeof(Tree));


        solve(1,n);

        int Max = -1;
        for(int i=1 ;i<=n ;i++){
           // printf("Ans[%d] = %d\n",i,Ans[i]);
            Max = max(Ans[i],Max);
        }

        printf("%d\n",Max);
        int pre = -1;
        for(int i=1 ;i<=n ;i++){
            if(Ans[i] == Max && (pre == -1 || (a[i].y <= a[pre].y && a[i].z >= a[pre].z))){
                Max--;
                printf("%d%c",i,(Max == 0?'\n':' '));
                pre = i;
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值