hdu 5324 Boring Class


题目描述:

给出两个序列L[i], R[i],求出最长的子序列v[i]满足:
v(1) ≥ 1, v(m) ≤ n,v(i) < v(i + 1). (for i from 1 to m - 1)
L(v(i)) ≥ L(v(i + 1)), R(v(i)) ≤ R(v(i + 1))(for i from 1 to m - 1)
n~1e5;

题解:

转化成求同时两个的最长单调不降子序列.
对于一维的求法:我们认为是(i, li)的单调不降子序列. 如果按照li从小到大的顺序往里面放,那么对于心将要往里放的i,来说, dp[i] = 维护的树状数组的从1到i的最大值. 同样,我们也可以按照i的顺序往里放,然后找<=li的树状数组中的最大值.
对于本题,其实是三维(i,li, ri). 如果我们按照i的顺序往里面放数,那么我们要找到的就是放在树套树结构中同时小于等于li和ri的最大的dp值.这样树套树的方法就有了:如果是两个线段树,那么先进入到1~li的区间的线段树节点来查询.进入节点内部之后: 要以ri为键值,来综合查找出一个最大的dp值.之后把线段树的节点有效值pushUp. 插入的话: 单点插入,一共是nlogn的空间复杂度.
独立于树套平衡树,我们还有一种更巧妙的方法来解这道题目.
对于(i,li,ri),想cdq分治.分治有什么好处呢? 只需要思考左端对右端的贡献就好,这样就会减少一维i!!!!!! 然后我们按照li的大小顺序把一个一个数放进去,用树状数组维护小于等于ri的最大的dp值,来+1作为dp[i]的值更新到树状数组中. 注意其中能够更新的只有mid+1到r的i, l到mid的i是不能更新的,只能放进去. 并且mid+1到r的是不能放到树状数组里面的. 和归并操作一样.这样完了之后再递归处理右边.

重点:

(1)想明白和以为的单调不降子序列的关系.以及熟练掌握用树状数组求单调不降子序列的思想,放进去的顺序的思维很好.
(2)用树状数组的思想来求3维不降子序列,考虑到树套树.
(3)三维减少一维的方法是分治.想明白cdq分治的影响部分.

代码:
cdq分治.需要注意的地方很多
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n;
struct node {
    int l, r, idx;
    friend bool operator < (node x, node y) {
        return x.l < y.l || (x.l == y.l && x.r < y.r) || (x.l == y.l && x.r == y.r && x.idx < y.idx);
    }
}p[50010];
int hs[50010];
struct ans_node {
    int sum, idx;
    ans_node(int _sum = 0, int _pre = 0) {
        sum = _sum;
        idx = _pre;
    }
    friend bool operator < (ans_node x, ans_node y) {
        return x.sum < y.sum || x.sum == y.sum && x.idx > y.idx;
    }
}C[50010], f[50010];
int ans, ans_pos, prepre[50010];
ans_node getMax(int x) {
    ans_node ret = ans_node(0, 0);
    while (x > 0) {
        ret = max(ret, C[x]);
        x -= x & -x;
    }
    return ret;
}
bool cmp(node x, node y) {
    return x.idx < y.idx;
}
void solve(int l, int r) {
    //printf("@#$%d %din\n", l, r);
    if (l == r) {
        if (f[l].sum == 0) {
            f[l].sum = 1;
            f[l].idx= l;
        }
        //printf("@#$%d %dout\n", l, r);
        return;
    }
    int mid = (l + r) >> 1;
    solve(mid + 1, r);
    int i = l, j = mid + 1;
    sort(p + l, p + mid + 1);
    sort(p + mid + 1, p + r + 1);
    int hs_len = 0;
    for (int i = mid + 1; i <= r; ++i) {
        hs[hs_len++] = p[i].r;
    }
    sort(hs, hs + hs_len);
    hs_len = unique(hs, hs + hs_len) - hs;
    for (int i = 0; i <= hs_len; ++i) {
        C[i] = 0;
    }
    for (int i = l; i <= mid; ++i) {
        int pos;
        while (j <= r && p[j].l <= p[i].l) {
            pos = lower_bound(hs, hs + hs_len, p[j].r) - hs + 1;
            while (pos <= hs_len) {
                C[pos] = max(C[pos], f[p[j].idx]);
                pos += pos & -pos;
            }
            j++;
        }
        pos = upper_bound(hs, hs + hs_len, p[i].r) - hs;
        ans_node max_nxt = getMax(pos), max_now = ans_node(f[p[i].idx].sum, prepre[p[i].idx]);
        max_nxt.sum++;
        if (max_now < max_nxt) {//注意,不能直接更新,因为以前的数可能会已经产生了影响.
            f[p[i].idx] = max_nxt;
            prepre[p[i].idx] = f[p[i].idx].idx;
            //printf("------%d %d\n", p[i].idx, f[p[i].idx].idx);
            f[p[i].idx].idx = p[i].idx;
        }
    }
    sort(p + l, p + mid + 1, cmp);
    solve(l, mid);
    //printf("@#$%d %dout\n", l, r);
}
int main() {
    //freopen("1009.in", "r", stdin);
    //freopen("out.txt", "w", stdout);
    while (~scanf("%d", &n)) {
        memset(f, 0, sizeof f);
        memset(prepre, 0, sizeof prepre);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &p[i].l);
        }
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &p[i].r);
            p[i].r = -p[i].r;
            p[i].idx = i;
        }
        solve(1, n);
        int ans = 0, ans_pos = 1000010;
        for (int i = 1; i <= n; ++i) {
            if (f[i].sum > ans || f[i].sum == ans && f[i].idx < ans_pos) {
                ans = f[i].sum;
                ans_pos = f[i].idx;
            }
        }
        printf("%d\n", ans);
        printf("%d", ans_pos);
        for (int k = prepre[ans_pos]; k > 0; k = prepre[k]) {
            printf(" %d", k);
        }
        printf("\n");
    }
    return 0;
}
///树套平衡树.  还没学习= = 这是别人的代码.
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>

    using namespace std;

    const int maxn = 50010;

    struct Node{
        Node *ch[2];
        int r;
        int v; //第二维
        int len; //长度
        int idx; //下标
        int mlen; //结点和结点左孩子右孩子里最大长度
        int midx; //结点和结点左孩子右孩子里最大长度对应的下标
        Node() {}
        Node(int v, int len, int idx):v(v),len(len),idx(idx),mlen(len),midx(idx) {ch[0] = ch[1] = NULL; r = rand();}

        int cmp(int x) const {
            if (x == v) return -1;
            return x < v ? 0 : 1;
        }

        void maintain(){

            mlen = len;
            midx = idx;

            if (ch[0] != NULL && (ch[0]->mlen > mlen || (ch[0]->mlen == mlen && ch[0]->midx < midx))){
                mlen = ch[0]->mlen;
                midx = ch[0]->midx;
            }

            if (ch[1] != NULL && (ch[1]->mlen > mlen || (ch[1]->mlen == mlen && ch[1]->midx < midx))){
                mlen = ch[1]->mlen;
                midx = ch[1]->midx;
            }
        }
    };

    bool findMax(Node* a, Node* b){
        if (a->mlen < b->mlen || (a->mlen == b->mlen && a->midx > b->midx)){
            *a = *b;
            return true;
        }
        return false;
    }

    namespace Treap{

        int cntnode;

        Node node[maxn*10];

        void init(){
            cntnode = 0;
        }

        Node* newNode(int v, int len, int idx){

            node[++cntnode] = Node(v, len, idx);
            return &node[cntnode];
        }

        void rotate(Node* &o, int d){

            Node* k = o->ch[d^1]; o->ch[d^1] = k->ch[d]; k->ch[d] = o;
            o->maintain(); k->maintain(); o = k;
        }

        void insert(Node* &o, int v, int len, int idx){

            if (o == NULL) o = newNode(v, len, idx);
            else {
                int d = o->cmp(v);
                if (d != -1){
                    insert(o->ch[d], v, len, idx);
                    if (o->r < o->ch[d]->r) rotate(o, d^1);
                }
                else
                {
                    if (len >= o->len){
                        o->len = len;
                        o->idx = idx;
                    }
                }
            }
            o->maintain();
        }

        Node search(Node *o, int v){
            if (o == NULL){
                return Node(-1, 0, -1);
            }
            else{
                Node re, tmp;
                if (o->v == v) {
                    re = Node(o->v, o->len, o->idx);
                    if (o->ch[1]){
                        findMax(&re, o->ch[1]);
                    }
                }
                else if (o->v > v){
                    re = Node(o->v, o->len, o->idx);
                    if (o->ch[1]){
                        findMax(&re, o->ch[1]);
                    }
                    if (o->ch[0]){
                        tmp = search(o->ch[0], v);
                        findMax(&re, &tmp);
                    }
                }
                else{
                    re = search(o->ch[1], v);
                }
                return re;
            }
        }

    }

    namespace BIT{

        Node* fwt[maxn];
        int N;

        void init(int n){
            N = n;
            memset(fwt, 0, sizeof fwt);
        }

        void add(int v1, int v2, int len, int idx){
            while(v1 < N){
                Treap::insert(fwt[v1], v2, len, idx);
                v1 += (-v1)&v1;
            }
        }

        Node query(int v1, int v2){
            Node re, tmp;
            re = Node(-1, 0, -1);
            while(v1 > 0){
                tmp = Treap::search(fwt[v1], v2);
                findMax(&re, &tmp);
                v1 -= (-v1)&v1;
            }
            return re;
        }

    }

    struct Pe{
        int L,R;
        int i;
        bool operator < (const Pe& rhs)const{
            return L < rhs.L;
        }
    };

    bool cmp(Pe a, Pe b){
        return a.i < b.i;
    }

    int solo[maxn];
    Pe pe[maxn];int pre[maxn];

    void print(Node& a){
        int id = a.midx;
        printf("%d\n", a.mlen);
        while(1){
            printf("%d", id+1);
            if (pre[id] == -1){
                break;
            }
            printf(" ");
            id = pre[id];
        }
        printf("\n");
    }

    int main(){

        int n;
        while(scanf("%d", &n) != EOF){

            for(int i=0;i<n;i++){
                pe[i].i = i;
                scanf("%d", &pe[i].L);
            }
            for(int i=0;i<n;i++){
                scanf("%d", &pe[i].R);
            }
            sort(pe, pe+n);
            int m = 0;
            solo[0] = ++m;
            for(int i=1;i<n;i++){
                if (pe[i].L != pe[i-1].L){
                    solo[i] = ++m;
                }
                else{
                    solo[i] = solo[i-1];
                }
            }
            for(int i=0;i<n;i++){
                pe[i].L = solo[i];
            }
            sort(pe, pe+n, cmp);

            BIT::init(m+1);
            Treap::init();
            Node ans = Node(-1, 0, -1), tmp;

            for(int i=n-1;i>=0;i--){
                tmp = BIT::query(pe[i].L, pe[i].R);
                pre[i] = tmp.midx;
                tmp.mlen = tmp.mlen + 1;
                tmp.midx = i;
                BIT::add(pe[i].L, pe[i].R, tmp.mlen, tmp.midx);
                findMax(&ans, &tmp);
            }

            print(ans);
        }

        return 0;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值