codeforces 777E. Hanoi Factory (LIS+dp+树状数组/线段树维护区间最大值及单点更新)

E. Hanoi Factory
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Of course you have heard the famous task about Hanoi Towers, but did you know that there is a special factory producing the rings for this wonderful game? Once upon a time, the ruler of the ancient Egypt ordered the workers of Hanoi Factory to create as high tower as possible. They were not ready to serve such a strange order so they had to create this new tower using already produced rings.

There are n rings in factory's stock. The i-th ring has inner radius ai, outer radius bi and height hi. The goal is to select some subset of rings and arrange them such that the following conditions are satisfied:

  • Outer radiuses form a non-increasing sequence, i.e. one can put the j-th ring on the i-th ring only if bj ≤ bi.
  • Rings should not fall one into the the other. That means one can place ring j on the ring i only if bj > ai.
  • The total height of all rings used should be maximum possible.
Input

The first line of the input contains a single integer n (1 ≤ n ≤ 100 000) — the number of rings in factory's stock.

The i-th of the next n lines contains three integers aibi and hi (1 ≤ ai, bi, hi ≤ 109bi > ai) — inner radius, outer radius and the height of the i-th ring respectively.

Output

Print one integer — the maximum height of the tower that can be obtained.

Examples
input
3
1 5 1
2 6 2
3 7 3
output
6
input
4
1 2 1
1 3 3
4 6 2
5 7 1
output
4
Note

In the first sample, the optimal solution is to take all the rings and put them on each other in order 321.

In the second sample, one can put the ring 3 on the ring 4 and get the tower of height 3, or put the ring 1 on the ring 2 and get the tower of height 4.

题目给出了一堆指环的内径a、外径b、高h,要把指环叠成塔,
即外径从下往上不递增,同时两个上下相邻的指环中上面的指环
的外径要大于下面指环的内径(不然上面的指环就掉下去了),
求最高的塔。


求塔,即求一个从下往上不递增的序列,那么就可能可以借鉴最长上升子序列(LIS)
的思路了,即设dp[i]为以指环i为终点(这里的终点可以指塔顶)所能得到的
最高的塔的高度,然后状态方程为
dp[i] = max(dp[j]) + h(i) ,( 1<=j<=n, b(j) >= b(i) ,a(j) < b(i) ) 
将指环以b(i)从大到小的顺序排序优化下,可得
dp[i] = max(dp[j]) + h(i), ( 1<=j 这样的时间复杂度为O(n^2),还是过大,那么可以考虑用数据结构去维护
max(dp[j])(1<=j 的dp[i],使得下一轮找a(j) 因为每轮查询都要查询a(j) 考虑先将所有a(i)提取出来并排序离散化为单调数组,然后每次查询就可用
b(i)去二分查询单调数组,得到单调离散数组的一个下标。
也就是拿单调离散数组的下标去映射a[i]。
然后就只需要用树状数组/线段树去维护区间最大值了。
------------------
一开始用线段树不小心写成了区间更新,还加上了lazy操作,wa了几次。。
改成单点更新就过了。。

//树状数组
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define FOP freopen("data.txt","r",stdin)
#define FOP2 freopen("data1.txt","w",stdout)
#define inf_LL 4223372036854775807
#define inf 0x3f3f3f3f
#define maxn 100010
#define mod 1000000007
#define PI acos(-1.0)
#define LL long long
using namespace std;

LL Max(LL a, LL b) { return a < b ? b : a; }

struct Node
{
    int a, b, h;
    Node(int _a = 0, int _b = 0, int _h = 0) : a(_a), b(_b), h(_h) {}
}node[maxn], ring[maxn];

bool cmp(Node n1, Node n2) { return n1.b > n2.b; }

int n, m, M;
LL dp[maxn];
int ha[maxn];

int maxVal[maxn], c[maxn];

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

void update(int x, int val, int n)
{                                  
    c[x] = val;
    for (int i = x; i <= n; i += lowbit(i))
    {
        maxVal[i] = c[i];
        for (int j = 1; j < lowbit(i); j <<= 1)
        {
            maxVal[i] = Max(maxVal[i], maxVal[i - j]);
        }
    }
}

LL getMax(int l, int r)
{
    LL res = c[r];
    while (l != r)
    {
        for (r -= 1; r - lowbit(r) >= l; r -= lowbit(r))
        {
            res = Max(res, maxVal[r]);
        }
        res = Max(res, c[r]);
    }
    return res;
}

int main()
{
    while(~scanf("%d", &n))
    {
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++) scanf("%d%d%d", &node[i].a, &node[i].b, &node[i].h);
        sort(node+1, node+n+1, cmp);

        m = 0;
        ring[++m] = node[1];
        for(int i = 2; i <= n; i++)
        {
            if(node[i].b == node[i-1].b) ring[m].a = min(ring[m].a, node[i].a), ring[m].h += node[i].h;
            else ring[++m] = node[i];
        }

        for(int i = 1; i <= m; i++) ha[i] = ring[i].a;
        sort(ha+1, ha+m+1);

        M = unique(ha+1, ha+1+m) - (ha+1);


        for(int i = 1; i <= m; i++)
        {
            int t = lower_bound(ha+1, ha+1+M, ring[i].b) - (ha+1);
            LL ma = 0;
            if(t <= M && t >= 1) ma = getMax(1, t);
            dp[i] = ma + ring[i].h;
            t = lower_bound(ha+1, ha+1+M, ring[i].a) - ha;
            update(t, dp[i], M);
        }

        LL ans = 0;
        for(int i = 1; i <= m; i++) ans = Max(ans, dp[i]);
        printf("%lld\n", ans);
    }
    return 0;
}


//线段树,多了无意义的lazy操作。。
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define FOP freopen("data.txt","r",stdin)
#define FOP2 freopen("data1.txt","w",stdout)
#define inf_LL 4223372036854775807
#define inf 0x3f3f3f3f
#define maxn 100010
#define mod 1000000007
#define PI acos(-1.0)
#define LL long long
using namespace std;

LL Max(LL a, LL b) { return a < b ? b : a; }

struct Node
{
    int a, b, h;
    Node(int _a = 0, int _b = 0, int _h = 0) : a(_a), b(_b), h(_h) {}
}node[maxn], ring[maxn];

bool cmp(Node n1, Node n2) { return n1.b > n2.b; }

int n, m;
LL dp[maxn];
int ha[maxn];

struct TreeNode
{
    int l, r;
    LL ma;
    int mid() { return l+r>>1; }
}tree[maxn<<2];

void build(int l, int r, int rt)
{
    tree[rt].l = l, tree[rt].r = r, tree[rt].ma = 0;
    if(l == r) return ;
    int mid = l+r>>1;
    build(l, mid, rt<<1);
    build(mid+1, r, rt<<1|1);
}

LL setv[maxn<<2];

void pushdown(int rt)
{
    if(setv[rt])
    {
        setv[rt<<1] = setv[rt<<1|1] = setv[rt];
        setv[rt] = 0;
    }
}

void maintain(int rt)
{
    if(tree[rt].r > tree[rt].l) tree[rt].ma = Max(tree[rt<<1].ma, tree[rt<<1|1].ma);
    tree[rt].ma = Max(tree[rt].ma, setv[rt]);
}

void update(LL c, int L, int R, int rt)
{
    if(tree[rt].l >= L && tree[rt].r <= R)
    {
        if(c >= tree[rt].ma) setv[rt] = c;
    }
    else
    {
        pushdown(rt);
        int mid = tree[rt].mid();
        if(L <= mid) update(c, L, R, rt<<1); else maintain(rt<<1);
        if(R >= mid+1) update(c, L, R, rt<<1|1); else maintain(rt<<1|1);
    }
    maintain(rt);
}

LL ma;
void query(int L, int R, int rt)
{
    if(setv[rt] > 0) ma = Max(ma, setv[rt]);
    else if(tree[rt].l >= L && tree[rt].r <= R) ma = Max(ma, tree[rt].ma);
    else
    {
        int mid = tree[rt].mid();
        if(L <= mid) query(L, R, rt<<1);
        if(R >= mid+1) query(L, R, rt<<1|1);
    }
}

int main()
{
    while(~scanf("%d", &n))
    {
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++) scanf("%d%d%d", &node[i].a, &node[i].b, &node[i].h);
        sort(node+1, node+n+1, cmp);

        m = 0;
        ring[++m] = node[1];
        for(int i = 2; i <= n; i++)
        {
            if(node[i].b == node[i-1].b) ring[m].a = min(ring[m].a, node[i].a), ring[m].h += node[i].h;
            else ring[++m] = node[i];
        }

        for(int i = 1; i <= m; i++) ha[i] = ring[i].a;
        sort(ha+1, ha+m+1);
        int M = unique(ha+1, ha+1+m) - (ha+1);

        build(1, M, 1);

        for(int i = 1; i <= m; i++)
        {
            int t = lower_bound(ha+1, ha+1+M, ring[i].b) - (ha+1);
            ma = 0;
            if(t <= M && t >= 1) query(1, t, 1);
            dp[i] = ma + ring[i].h;
            t = lower_bound(ha+1, ha+1+M, ring[i].a) - ha;
            //update(dp[i], t, M, 1);
            update(dp[i], t, t, 1);
        }

        LL ans = 0;
        for(int i = 1; i <= m; i++) ans = Max(ans, dp[i]);
        printf("%lld\n", ans);
    }
    return 0;
}



你可能感兴趣的:(codeforces 777E. Hanoi Factory (LIS+dp+树状数组/线段树维护区间最大值及单点更新))