(简单线段树,简单dp,简单二分)Codeforces Round #401 (Div. 2) E. Hanoi Factory

分类:数据结构,DP,二分查找

cf difficulty:2000

题面链接:https://codeforces.com/contest/777/problem/E

题意:

N个汉诺塔 三个属性 小半径 A,大半径B,高度

需要满足条件

对于每个汉诺塔 上面的B需要小于下面的B 并且 上面的B要大于下面的A

问能叠成的最大高度为多少

 

思路:

由于汉诺塔的B必须从上到下越来越大,我们将涵洛塔基于B排序。维护一个DP,表示选了第i个汉诺塔他和他上面的汉诺塔最多能形成多大的高度,答案即是 max(dp[i])

从1到n遍历我们每次二分找出比当前汉诺塔的A大的B在第几个(id表示)。在用线段树找出(id,i-1)最大的dp;最终可知dp[i]=maxdp+h (h为当前汉诺塔的高度)

 

总结:

由于长期没写线段树 一个简单的max线段树 出现多个初学者才写的bug

 

#include <bits/stdc++.h>
 
using namespace std;
typedef pair<int,int> P;
typedef struct{
    int a,b,h;
} san;
typedef long long ll;
 
 
san arr1[100050];
ll dp[100050];
ll tree[100050*4];
int n;
int n_ = 1;
 
ll qu(int a,int b,int l,int r,int o){
    ll res = 0;
    if(a>=r || b<=l)    return 0;
    if(l>=a && r<=b)    return tree[o];
 
    int m =(l+r)/2;
 
    res = max(res,qu(a,b,l,m,o*2+1));
    res = max(res,qu(a,b,m,r,o*2+2));
 
    return res;
}
 
void up(int index,ll num){
    index += n_-1;
    tree[index] = num;
    while(index){
        index = (index-1)/2;
        tree[index] = max(tree[index*2+1],tree[index*2+2]);
    }
}
 
void init(){
    while(n_<n) n_*=2;
}
 
int check(int id,int a){
    int b = arr1[id].b;
    if(b>a)    return 1;
    return 0;
}
 
int cmp(san aa,san bb){
    if(aa.b == bb.b){
        return aa.a < bb.a;
    }
    return aa.b<bb.b;
}
 
 
int main()
{
    cin>>n;
 
    for(int i=1;i<=n;i++){
        int a,b,h;
        scanf("%d%d%d",&a,&b,&h);
        arr1[i].b = b;
        arr1[i].a = a;
        arr1[i].h = h;
    }
 
    sort(arr1+1,arr1+1+n,cmp);
 
    for(int i=1;i<=n;i++){
       // printf("a=%d b=%d h=%d\n",arr1[i].a,arr1[i].b,arr1[i].h);
    }
    init();
    //reverse(arr,arr+n);
 
    ll ans =0 ;
    for(int i=1;i<=n;i++){
        int a = arr1[i].a;
        int b = arr1[i].b;
        ll h = arr1[i].h;
 
        int l=0;
        int r=i-1;
        while(l<=r){
            int m= (l+r)/2;
            if(check(m,a))r = m-1;
            else l=m+1;
        }
 
        int id = r+1;
        if(id==i)   id=0;
 
        ll num=0;
        if(id)  num = qu(id-1,i-1,0,n_,0);
        dp[i] = num + h;
       // printf("id=%d num=%lld dp=%lld\n",id,num,dp[i]);
        ans = max(ans,dp[i]);
 
        up(i-1,dp[i]);
        //printf("i=%d dp=%lld %d\n",i,dp[i],qu(0,i,0,n_,0));
 
    }
 
 
 
 
    cout<<ans;
 
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值