【cdq分治+DP】HDU5324 Boring Class

S o u r c e : Source: Source:2015 Multi-University Training Contest 3
P r o b l e m : Problem: Problem:给两个长度为n=5e4的序列,A和B,要求在A序列中找一个非递增的子序列,同时这个子序列的下标在B串中非递增。要求这个子序列最长,输出字典序最小的方案。
I d e a : Idea: Idea:经典的三维偏序,CDQ分治。由于要求字典序最小,所以从后往前更新。然后只是前缀最大值的话,直接树状数组维护下就好了。可以维护值最大且标号最小的后继点,也可以只维护最大值,在输出时考虑后继点就好了。
(一些cdq的问题:1.下标各不相同,直接就可以拿来做分治的拿一维。剩下的两维按照其中一维排序后就是二维偏序了。2.如果是先更新完一个区间再更新另一个,下标会被打乱,要重新sort。)

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

#define lc o<<1
#define rc o<<1|1
#define fi first
#define se second
#define pb push_back
#define ALL(X) (X).begin(), (X).end()
#define bcnt(X) __builtin_popcountll(X)
#define CLR(A, X) memset(A, X, sizeof(A))
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define DEBUG printf("Passing [%s] in Line %d\n",__FUNCTION__,__LINE__)

using DB = double;
using LL = long long;
using PII = pair<int, int>;
const int N = 1e5+10;
//const LL MOD = 998244353;
//const LL INF = 1e18;

int c[N], dp[N], nxt[N];
inline int query(int x) {
    int id = 0;
    while(x) {
        if(!id || dp[c[x]]>dp[id] || (dp[id]==dp[c[x]]&&id>c[x])) {
            id = c[x];
        }
        x-=(x&-x);
    }
    return id;
}
inline void add(int x, int id) {
    while(x < N) {
        if(!c[x] || dp[id]>dp[c[x]] || (dp[id]==dp[c[x]]&&id<c[x])) {
            c[x] = id;
        }
        x+=(x&-x);
    }
}
inline void del(int x) { while(x < N) c[x] = 0, x+=(x&-x); }

struct Query {
    int x, y, z;
    bool operator < (const Query &A) const {
        return x==A.x?y>A.y:x<A.x;
    }
}q[N], tq[N];

void solve(int L, int R) {
    if(L == R) return;
    int M = L+R>>1, l1 = L, l2 = M+1;
    for(int i = L; i <= R; i++) {
        if(q[i].z <= M) tq[l1++] = q[i];
        else tq[l2++] = q[i];
    }
    for(int i = L; i <= R; i++) q[i] = tq[i];
    solve(M+1, R);
    sort(q+M+1, q+R+1);
    int j = R;
    for(int i = M; i >= L; i--) {
        while(j>M && q[j].x>=q[i].x) add(q[j].y, q[j].z), j--;
        int v = query(q[i].y);
        int u = q[i].z;
        if(dp[v]+1>dp[u] || (dp[v]+1==dp[u]&&v<nxt[u])) {
            dp[u] = dp[v]+1;
            nxt[u] = v;
        }
    }
    for(int i = R; i > j; i--) del(q[i].y);
    solve(L, M);
}

int b[N];

int main() {
    int n;
    while(~scanf("%d", &n)) {
        int m = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &q[i].y);
            b[m++] = q[i].y;
            nxt[i] = 0; dp[i] = 1;
        }
        sort(b, b+m);
        m = unique(b, b+m)-b;
        for(int i = 1; i <= n; i++) {
            q[i].y = lower_bound(b, b+m, q[i].y)-b+1;
            scanf("%d", &q[i].x);
            q[i].z = i;
        }
        sort(q+1, q+n+1);
        solve(1, n);
        int id = 1;
        for(int i = 2; i <= n; i++) {
            if(dp[i] > dp[id]) id = i;
        }
        printf("%d\n", dp[id]);
        while(id) {
            printf("%d%c", id, nxt[id]?' ':'\n');
            id = nxt[id];
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值