分类:数据结构,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;
}