Codeforces Round #271 (Div. 2) E. Pillars(线段树优化DP)

题目链接:点击打开链接

题意:一个n个数的序列,每个数有一个高度值h[i]。 求一个最长子序列,要求相邻两个数满足| h[i] - h[i-1] | >= d。 并要求打印出该序列。

类似于最长上升子序列, DP思想很简单, 但是可惜n太大了, 二重循环会超时。 所以要想办法优化掉第二层循环。

观察发现, 第二层所做的事情就是在所有满足j<i && | h[i] - h[j] | >= d 的j中找到最大的d[j]。

j < i很容易满足, 按照顺序加入就可以了, 但是还要满足大小关系, 怎么办呢?

把不等式变形就成了: h[j] <= h[i] - d 或者 h[j] >= h[i] + d 。 这不就是一个区间吗?  所以我们可以以高度为区间建一棵线段树,由于h[i]太大,先离散化一下。

可见, 线段树的区间下标并不是没有用的, 适当利用, 也可以用来维护一些信息。

细节参见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int mod = 1000000000 + 7;
const int INF = 1000000000;
const int maxn = 100000 + 10;
int T,m,dd,n,d[maxn],pre[maxn];
ll a[maxn],b[maxn];
struct node {
    int v, id;
}maxv[maxn<<2];
void PushUp(int o) {
    if(maxv[o<<1].v <= maxv[o<<1|1].v) maxv[o] = maxv[o<<1|1];
    else maxv[o] = maxv[o<<1];
}
void build(int l, int r, int o) {
    int m = (l + r) >> 1;
    maxv[o].v = 0;
    if(l == r) return ;
    build(l, m, o<<1);
    build(m+1, r, o<<1|1);
}
void update(int L, int R, int v, int l, int r, int o) {
    int m = (l + r) >> 1;
    if(L <= l && r <= R) {
        if(maxv[o].v < d[v]) {
            maxv[o].v = d[v];
            maxv[o].id = v;
        }
        return ;
    }
    if(L <= m) update(L, R, v, l, m, o<<1);
    if(m < R) update(L, R, v, m+1, r, o<<1|1);
    PushUp(o);
}
node query(int L, int R, int l, int r, int o) {
    int m = (l + r) >> 1;
    if(L <= l && r <= R) {
        return maxv[o];
    }
    node ans ; ans.v = 0;
    if(L <= m) {
        node v = query(L, R, l, m, o<<1);
        if(ans.v < v.v) ans = v;
    }
    if(m < R) {
        node v = query(L, R, m+1, r, o<<1|1);
        if(ans.v < v.v) ans = v;
    }
    PushUp(o);
    return ans;
}
void print(int root) {
    if(d[root] == 0) return ;
    else print(pre[root]);
    if(pre[root] == 0) printf("%d",root);
    else printf(" %d",root);
}
int main() {
        scanf("%d%d",&n,&dd);
        for(int i=1;i<=n;i++) {
            scanf("%I64d",&a[i]);
            b[i] = a[i];
        }
        sort(b+1, b+1+n);
        int m = unique(b+1, b+1+n) - b - 1;
        d[1] = 0;
        build(1, m, 1);
        int ans = 0, root = -1;
        for(int i=1;i<=n;i++) {
            d[i] = 0;
            int L = upper_bound(b+1, b+1+m, a[i] - dd) - b;
            int R = lower_bound(b+1, b+1+m, a[i] + dd) - b;
            L--;
            if(L >= 1) {
                node cur = query(1, L, 1, m, 1);
                if(cur.v + 1 > d[i]) {
                    d[i] = cur.v + 1;
                    pre[i] = cur.id;
                }
            }
            if(R <= m) {
                node cur = query(R, m, 1, m, 1);
                if(cur.v + 1 > d[i]) {
                    d[i] = cur.v + 1;
                    pre[i] = cur.id;
                }
            }
            int v = lower_bound(b+1, b+1+m, a[i]) - b;
            update(v, v, i, 1, m, 1);
            if(ans < d[i]) {
                ans = d[i] ;
                root = i;
            }
        }
        if(ans == 0) { printf("0\n"); return 0; }
        printf("%d\n",ans);
        print(root);
        printf("\n");
    return 0;
}


你可能感兴趣的:(dp,线段树,离散化,codeforces,ACM-ICPC)