CF709 C. Skyline Photo(线段树优化DP)
CF709 C. Skyline Photo(线段树优化DP)
题意:给两个数组,分别表示分数和高度,你可以将数组分成若干段,每段贡献为其高度最低的分数,求最高分数和。
题解:
好题,首先先考虑n*n做法,f[i]表示将前i段分成若干段的最大值,则f[n]即表示答案,易得递推方程:f[i]=max(f[i], f[j]+区间最低高度的分数)
所以我们便可以用线段树优化,处理区间最低高度的分数与f[j]的最大值。
然后每次循环开始时,第i个高度,会使一段区间的最低高度改变,二分线段树找到最远影响位置,同时区间修改影响的高度与分数,易知分f的值不会改变,所以只需储存区间内f的最大值,在区间修改完分数后,区间最大值就是分数+f的最大值。
#include<iostream>
using namespace std;
#define ll long long
const ll N=3e5+7;
const ll inf=1e9;
ll n,b[N],a[N];
struct madoka{
ll l;
ll r;
ll f;
ll b;
ll dp;
ll sum;
}ma[N*4];
void build(ll l,ll r,ll k){
ma[k].l=l;
ma[k].r=r;
if(l==r){
ma[k].b=inf;
return;
}
ll mid=(l+r)/2;
build(l,mid,k*2);
build(mid+1,r,k*2+1);
ma[k].b=max(ma[k*2].b,ma[k*2+1].b);
}
void down(ll k){
if(ma[k].f&&ma[k].l!=ma[k].r){
ma[k*2].f=ma[k].f;
ma[k*2].b=b[ma[k].f];
ma[k*2].sum=ma[k*2].dp+a[ma[k].f];
ma[k*2+1].f=ma[k].f;
ma[k*2+1].b=b[ma[k].f];
ma[k*2+1].sum=ma[k*2+1].dp+a[ma[k].f];
ma[k].f=0;
}
}
ll fin(ll k,ll h){
down(k);
if(ma[k].l==ma[k].r){
return ma[k].l;
}
ll mid=(ma[k].l+ma[k].r)/2;
if(ma[k*2].b>=h){
return fin(k*2,h);
}
else{
return fin(k*2+1,h);
}
}
void up(ll k,ll l,ll r){
down(k);
if(l<=ma[k].l&&ma[k].r<=r){
ma[k].b=b[r];
ma[k].sum=a[r]+ma[k].dp;
ma[k].f=r;
return;
}
ll mid=(ma[k].l+ma[k].r)/2;
if(l<=mid){
up(k*2,l,r);
}
if(mid<r){
up(k*2+1,l,r);
}
ma[k].dp=max(ma[k*2].dp,ma[k*2+1].dp);
ma[k].sum=max(ma[k*2].sum,ma[k*2+1].sum);
ma[k].b=max(ma[k*2].b,ma[k*2+1].b);
}
void up_dp(ll k,ll p,ll z){
down(k);
if(ma[k].l==ma[k].r){
ma[k].dp=z;
return;
}
ll mid=(ma[k].l+ma[k].r)/2;
if(p<=mid){
up_dp(k*2,p,z);
}
else{
up_dp(k*2+1,p,z);
}
ma[k].dp=max(ma[k*2].dp,ma[k*2+1].dp);
ma[k].sum=max(ma[k*2].sum,ma[k*2+1].sum);
ma[k].b=max(ma[k*2].b,ma[k*2+1].b);
}
ll qry(ll k,ll l,ll r){
down(k);
if(l<=ma[k].l&&ma[k].r<=r){
return ma[k].sum;
}
ll mid=(ma[k].l+ma[k].r)/2;
ll res=-inf;
if(l<=mid){
res=max(res,qry(k*2,l,r));
}
if(mid<r){
res=max(res,qry(k*2+1,l,r));
}
return res;
}
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&b[i]);
}
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
build(1,n,1);
for(int i=1;i<=n;i++){
ll p=fin(1,b[i]);
up(1,p,i);
ll mx=qry(1,1,i);
up_dp(1,i+1,mx);
if(i==n){
printf("%lld\n",mx);
}
}
}