hdu 5324 Boring Class cdq分治+树状数组+离散化

题意:

给你两个序列,两个序列都有n个数。然后让你找到一个位置序列,对应到两个序列上,使得新的L序列非递增,R序列非递减。

问你最长的长度是多少,并输出字典序最小的方案。


思路:

人生的第一道cdq分治,一直觉得这些类型学会了放在比赛也做不出来就没去学了。。。但看到辣么多人会做。。。赶紧搞起。

思路、代码盗自别人blog:http://blog.csdn.net/u013007900/article/details/47111319


由于值太大,先把L、R序列的值都离散化。

首先定义dp[i]:以第i个数为开头的最长长度是多少。(已经定义出dp状态,如果n^2能过,直接dp就可搞了hhh。)

由于要求字典序最小,因此要从后面搞起。如果从前面搞起,dp[i]状态就应该定义为 以第i个数结尾的最长长度,就算让你找到最长长度,但很难得知字典序最小的那个。


大概过程:

先把序列一分为二,既然是从后面搞起,则先把右区间递归搞掉,搞完右区间,然后利用树状数组更新对左区间的影响,清空树状数组后再递归搞左区间。(由于L序列要求非递增,则利用树状数组来保存比当前Li小的Lj的dp最大值。)

最后,我相信不会cdq的还是不知道怎么写,去看代码吧,少年。


code:

#include <bits/stdc++.h>
using namespace std;

const int N = 5e4+5;
typedef long long LL;

struct PP {
    int l, r;
    int id;
    bool operator < (const PP &cmp) const {
        if(r != cmp.r) return r < cmp.r;
        if(l != cmp.l) return l > cmp.l;    //nothing
        return id < cmp.id;
    }
}a[N];
PP p1[N], p2[N];

int n, tn;
int bit[N<<1];
int ls[N<<1];
int dp[N];

void update(int x, int val) {
    while(x <= tn) {
        bit[x] = max(bit[x], val);
        x += x&(-x);
    }
}
int query(int x) {
    int ret = 0;
    while(x > 0) {
        ret = max(ret, bit[x]);
        x -= x&(-x);
    }
    return ret;
}
void clear(int x) {
    while(x <= tn) {
        bit[x] = 0;
        x += x&(-x);
    }
}

void cdq(int l, int r) {
    if(l == r) {
        int &d = dp[a[l].id];
        d = max(d, 1);
        return ;
    }
    int mid = (l+r)>>1;
    cdq(mid+1, r);
    for(int i = mid+1;i <= r; i++) p2[i] = a[i];
    for(int i = l; i <= mid; i++) p1[i] = a[i];
    sort(p1+l, p1+mid+1);
    sort(p2+mid+1, p2+r+1);
    
    for(int i = mid, j = r;i >= l; i--) {
        while(j > mid && p2[j].r >= p1[i].r) {
            update(p2[j].l, dp[p2[j].id]);
            j--;
        }
        int &e = p1[i].id;
        dp[e] = max(dp[e], query(p1[i].l)+1);
    }

    for(int i = mid+1;i <= r; i++) clear(p2[i].l);
    cdq(l, mid);
}

void solve() {
    memset(dp, 0, sizeof(dp));
    memset(bit, 0, sizeof(bit));
    cdq(1, n);
    int maxv = -1;
    for(int i = 1;i <= n; i++) maxv = max(maxv, dp[i]);
    printf("%d\n", maxv);
    bool flag = false;
    int pre = 0;
    for(int i = 1;i <= n; i++) {
        if(dp[i] == maxv && (pre == 0||(a[pre].l>=a[i].l&&a[pre].r<=a[i].r))) {
            if(!flag) {
                flag = true;
                printf("%d", i);
            }
            else printf(" %d", i);
            maxv--;
            pre = i;
        }
    }
    puts("");
}
    
int main() {
    while(scanf("%d", &n) != EOF) {
        for(int i = 1;i <= n; i++) scanf("%d", &a[i].l);
        for(int i = 1;i <= n; i++) scanf("%d", &a[i].r);
        for(int i = 1;i <= n; i++) a[i].id = i;

        int cnt = 0;
        for(int i = 1;i <= n; i++) {
            ls[cnt++] = a[i].l;
            ls[cnt++] = a[i].r;
        }
        sort(ls, ls+cnt);
        cnt = unique(ls, ls+cnt)-ls;
        tn = cnt;
        for(int i = 1;i <= n; i++) {
            a[i].l = lower_bound(ls, ls+cnt, a[i].l)-ls+1;
            a[i].r = lower_bound(ls, ls+cnt, a[i].r)-ls+1;
        }

        solve();
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值