LOJ2611. NOIP2013 积木大赛 【线段树】

版权声明:欢迎转载,请注明出处,谢谢 https://blog.csdn.net/Dream_maker_yk/article/details/82532843

LOJ2611. NOIP2013 积木大赛


LINK


题目大意是给你一个目标状态数组
每次你可以选择一个连续区间加上一个值,求最小操作次数


我是神奇的脑子
最近做数据结构疯了
然后看见这题就数据结构了

好像网上还没有这种做法


逆向考虑这个过程
我们直接从目标数组删去一个连续区间

我们先考虑对于一个区间肯定一次删掉 m i n ( l   t o   r ) min(l\ to\ r) 是最优的情况
假设区间最小的位置是pos,那么删除后pos变成了0
所以可以递归成 l   t o   p o s 1 l\ to\ pos-1 p o s + 1   t o   r pos+1\ to\ r 两个区间
累加上pos的高度并且区间减这个高度就好了

因为每一次删除会把一个位置变成0,所以最多操作n次,然后时间复杂度是 n l o g n nlogn


#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;
}

猜你喜欢

转载自blog.csdn.net/Dream_maker_yk/article/details/82532843