版权声明:欢迎转载,请注明出处,谢谢 https://blog.csdn.net/Dream_maker_yk/article/details/82532843
LOJ2611. NOIP2013 积木大赛
题目大意是给你一个目标状态数组
每次你可以选择一个连续区间加上一个值,求最小操作次数
我是神奇的脑子
最近做数据结构疯了
然后看见这题就数据结构了
好像网上还没有这种做法
逆向考虑这个过程
我们直接从目标数组删去一个连续区间
我们先考虑对于一个区间肯定一次删掉
是最优的情况
假设区间最小的位置是pos,那么删除后pos变成了0
所以可以递归成
和
两个区间
累加上pos的高度并且区间减这个高度就好了
因为每一次删除会把一个位置变成0,所以最多操作n次,然后时间复杂度是 的
#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define LD t<<1
#define RD t<<1|1
int n,h[N];
int minv[N<<2],pos[N<<2],sub[N<<2];
void pushup(int t){
if(minv[LD]<=minv[RD]){
minv[t]=minv[LD],pos[t]=pos[LD];
}else{
minv[t]=minv[RD],pos[t]=pos[RD];
}
}
void pushdown(int t){
if(sub[t]){
minv[LD]-=sub[t];sub[LD]+=sub[t];
minv[RD]-=sub[t];sub[RD]+=sub[t];
sub[t]=0;
}
}
void build(int t,int l,int r){
if(l>r)return;
if(l==r){minv[t]=h[l];pos[t]=l;return;}
int mid=(l+r)>>1;
build(LD,l,mid);
build(RD,mid+1,r);
pushup(t);
}
void modify(int t,int l,int r,int L,int R,int vl){
if(l>r)return;
if(L<=l&&r<=R){minv[t]-=vl;sub[t]+=vl;return;}
pushdown(t);
int mid=(l+r)>>1;
if(R<=mid)modify(LD,l,mid,L,R,vl);
else if(mid<L)modify(RD,mid+1,r,L,R,vl);
else {
modify(LD,l,mid,L,mid,vl);
modify(RD,mid+1,r,mid+1,R,vl);
}
pushup(t);
}
#define pi pair<int,int>
pi query(int t,int l,int r,int L,int R){
if(l>r)return pi(0,0);
if(L<=l&&r<=R)return pi(minv[t],pos[t]);
pushdown(t);
int mid=(l+r)>>1;
pi ans;
if(mid>=R)ans=query(LD,l,mid,L,R);
else if(mid<L)ans=query(RD,mid+1,r,L,R);
else{
pi tl=query(LD,l,mid,L,mid);
pi tr=query(RD,mid+1,r,mid+1,R);
if(tl.first<=tr.first)ans=tl;
else ans=tr;
}
pushup(t);
return ans;
}
#define LL long long
LL solve(int l,int r){
if(l>r)return 0;
pi now=query(1,1,n,l,r);
modify(1,1,n,l,r,now.first);
return (LL)now.first+solve(l,now.second-1)+solve(now.second+1,r);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&h[i]);
build(1,1,n);
printf("%lld",solve(1,n));
return 0;
}