对于一组(a,b,h),寻找一个满足条件的排列:a[i+1]< b[i] <=b[i+1],使得∑h尽量大。
考虑动态规划,将数组按照b的大小排列,然后从上往下枚举圆环放在最下面。dp[i][j]=max{dp[i-1][k]}+h[i],其中k>a[i],j表示最下面的圆环的外直径,dp表示高度。考虑到数值在10^9范围内,需要离散化数据。考虑到需要频繁地求范围内最大值,考虑用线段树维护。
比较容易错的点:
1.排序的时候b相等的话还要按照a的大小排序,因为有可能有先用一个内环比较小的套在底下,再放一个内环比较大、外环一样大的。
2.由于题意特殊,只需要单点更新并查询(a,N]的最大值,不需要传统线段树那样递归,一个循环就能跑出来。前提是建完全二叉树。
3.数据大,注意除了dp数组以外结构体也需要开到longlong,最后输出的时候要%I64d。
#include<stdio.h>
#include<algorithm>
#include<map>
using namespace std;
const int n=131072;
int T,X[n*3],ind=0,jnd=0,M[n*2];
long long dp[n*4];
struct node{
int a,b;
long long k;
bool operator <(const node& B){
return b==B.b?a<B.a:b<B.b;
}
void read(){
a=lower_bound(M,M+ind,X[jnd++])-M;
b=lower_bound(M,M+ind,X[jnd++])-M;
k=X[jnd++];
}
}p[n];
long long getmax(int t){
t+=n*2-1;
long long res=dp[t];
do{
if(!(t&1))if(res<dp[t|1])res=dp[t|1];
}while(t>>=1);
return res;
}
void verify(int x,long long y){
int t=n*2+x-1;
dp[t]=y;
while(t>>=1){
dp[t]=max(dp[t<<1],dp[t<<1|1]);
}
}
int main(){
scanf("%d",&T);
for(int i=0;i<3*T;i++){
scanf("%d",X+i);
if(i%3!=2)M[ind++]=X[i];
}
sort(M,M+ind);
for(int i=0;i<T;i++)
p[i].read();
sort(p,p+T);
long long temp;
for(int i=0;i<T;i++){
temp=getmax(p[i].a+1)+p[i].k;
if(dp[p[i].b+2*n-1]<temp)verify(p[i].b,temp);
}
printf("%I64d",dp[1]);
return 0;
}