P5044-[IOI2018] meetings 会议【dp,笛卡尔树,线段树二分】

正题

题目链接:https://www.luogu.com.cn/problem/P5044


题目大意

给出一个长度为\(n\)的序列\(h\),定义\(dis(x,y)=max\{h_i\}(x\leq i\leq y)\)

\(q\)次询问给出一个区间\([L,R]\),找到一个\(x\in[L,R]\),最小化\(\sum_{i=L}^Rdis(i,x)\)的值。

\(1\leq n,q\leq 750000,1\leq h_i\leq 10^9\)


解题思路

先考虑暴力的做法,记\(f_{l,r}\)表示区间\([l,r]\)的答案,那么我们找出\([l,r]\)的任意一个最大值的位置\(x\),要么是左边跨过这个值要么是右边跨过这个值,也就是

\[f_{l,r}=min\{f_{l,x-1}+(r-x+1)\times h_x,f_{x+1,r}+(x-l+1)\times h_x\} \]

考虑怎么优化这个转移,因为关系到这个区间的最大值,所以我们考虑在笛卡尔树上计算答案。

对于每个询问\([L,R]\),我们先找到这个区间的最大值\(x\)把它拆分成\([L,x]\)\([x,R]\)的询问。

先考虑形如\([x,R]\)这一部分的询问,因为这一部分都满足最大值在最左边。

对于每个笛卡尔树上的节点\(mid\),记它的区间\([l,r]\),我们考虑维护所有的\(f_{l,x}\)其中\(x\in[l,r]\)

那么我们怎么转移这个东西

  • 对于\(x\in[l,mid-1]\)\(f_{l,x}\)代表的意义不变。
  • 对于\(x=mid\)\(f_{l,mid}=f_{l,mid-1}+h_{mid}\),因为\(mid\)是最高点,在这个点聚合显然不合适。
  • 对于\(x\in[mid+1,r]\)\(f_{l,x}=min\{f_{l,mid}+h_{mid}\times (x-mid),f_{mid+1,x}+h_{mid}\times (mid-l+1)\}\)

麻烦的是后面那个东西,我们考虑在线段树上维护所有的\(f_{?,x}\)\(?\)根据处理到的节点改变)。

然后发现\(f_{l,mid}+h_{mid}\times (x-mid)\)根据\(x\)的变化稳定增加\(h_{mid}\),而\(f_{mid+1,x}+h_{mid}\times (mid-l+1)\)这个玩意每次的增量是\(f_{mid+1,x}-f_{mid+1,x-1}\),显然是不会超过\(h_{mid}\)的。

所以选择它们两个的决策之间存在一个交点,我们可以二分这个位置。考虑这个加等差序列和区间推平操作都是要在线段树上搞的,所以我们直接顺手在线段树上二分就好了,因为这个的原因我们要记录一下左端点和右端点的权值。

时间复杂度:\(O(\ (n+q)\log n\ )\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const ll N=760000;
struct node{
	ll r,w,id;
};
ll n,m,lg[N],h[N],f[N][20],ans[N];
ll ql[N],qr[N];vector<node> q[N];
struct SegTree{
	#define M N<<2
	ll w[M],k[M],c[M],wl[M],wr[M];
	void Clear(){
		memset(w,0,sizeof(w));
		memset(wl,0,sizeof(wl));
		memset(wr,0,sizeof(wr));
		memset(k,0,sizeof(k));
		memset(c,0,sizeof(c));
	}
	void Setw(ll x,ll val){
		w[x]=wl[x]=wr[x]=val;
		k[x]=c[x]=0;return;
	}
	void Setk(ll x,ll val){
		wl[x]+=val;wr[x]+=val;
		k[x]+=val;return;
	}
	void Downdata(ll x,ll L,ll R){
		if(w[x]>0)Setw(x*2,w[x]),Setw(x*2+1,w[x]),w[x]=-1;
		if(k[x])Setk(x*2,k[x]),Setk(x*2+1,k[x]),k[x]=0;
		if(c[x]){
			ll mid=(L+R)>>1;k[x*2+1]+=c[x]*(mid-L+1);
			wr[x*2]+=c[x]*(mid-L);wl[x*2+1]+=c[x]*(mid-L+1);
			wr[x*2+1]+=c[x]*(R-L);c[x*2]+=c[x];c[x*2+1]+=c[x];
			c[x]=0;
		}
		return;
	}
	void Change(ll x,ll L,ll R,ll l,ll r,ll val){
		if(L==l&&R==r){Setw(x,val);return;}
		ll mid=(L+R)>>1;Downdata(x,L,R);
		if(r<=mid)Change(x*2,L,mid,l,r,val);
		else if(l>mid)Change(x*2+1,mid+1,R,l,r,val);
		else Change(x*2,L,mid,l,mid,val),Change(x*2+1,mid+1,R,mid+1,r,val);
		wl[x]=wl[x*2];wr[x]=wr[x*2+1];return;
	}
	void Update(ll x,ll L,ll R,ll l,ll r,ll ks,ll cs){
		if(L==l&&R==r){Setk(x,ks);c[x]+=cs;wr[x]+=(R-L)*cs;return;}
		ll mid=(L+R)>>1;Downdata(x,L,R);
		if(r<=mid)Update(x*2,L,mid,l,r,ks,cs);
		else if(l>mid)Update(x*2+1,mid+1,R,l,r,ks,cs);
		else Update(x*2,L,mid,l,mid,ks,cs),Update(x*2+1,mid+1,R,mid+1,r,ks,cs);
		wl[x]=wl[x*2];wr[x]=wr[x*2+1];return;
	}
	ll Ask(ll x,ll L,ll R,ll pos){
		if(L==R)return wr[x];
		ll mid=(L+R)>>1;Downdata(x,L,R);
		if(pos<=mid)return Ask(x*2,L,mid,pos);
		return Ask(x*2+1,mid+1,R,pos);
	}
	void Solve(ll x,ll L,ll R,ll l,ll r,ll k1,ll b1,ll b2){
		if(L==l&&R==r){
			if(b1>=wl[x]+b2)
			{Setk(x,b2);return;}
			if(k1*(R-L)+b1<=wr[x]+b2){
				Setw(x,b1);
				wr[x]+=k1*(R-L);c[x]+=k1;
				return;
			}
		}
		ll mid=(L+R)>>1;Downdata(x,L,R);
		if(r<=mid)Solve(x*2,L,mid,l,r,k1,b1,b2);
		else if(l>mid) Solve(x*2+1,mid+1,R,l,r,k1,b1,b2);
		else Solve(x*2,L,mid,l,mid,k1,b1,b2),
		Solve(x*2+1,mid+1,R,mid+1,r,k1,b1+k1*(mid-l+1),b2);
		wl[x]=wl[x*2];wr[x]=wr[x*2+1];return;
	}
	#undef M
}T;
ll RMQ(ll l,ll r){
	ll z=lg[r-l+1];
	l=f[l][z];r=f[r-(1<<z)+1][z];
	return (h[l]<h[r])?r:l;
}
void solve(ll L,ll R){
	if(L>R)return;
	ll x=RMQ(L,R);
	solve(L,x-1);
	solve(x+1,R);
	for(ll i=0;i<q[x].size();i++)
		ans[q[x][i].id]=min(ans[q[x][i].id],q[x][i].w+T.Ask(1,1,n,q[x][i].r));
	ll f=(x>L)?T.Ask(1,1,n,x-1):0;
	f+=h[x];T.Change(1,1,n,x,x,f);
	if(x<R)T.Solve(1,1,n,x+1,R,h[x],f+h[x],(x-L+1)*h[x]);
	return;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(ll i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
	for(ll i=1;i<=n;i++)
		scanf("%lld",&h[i]),f[i][0]=i;
	memset(ans,0x3f,sizeof(ans));	
	
	for(ll j=1;(1<<j)<=n;j++)
		for(ll i=1;i+(1<<j)-1<=n;i++){
			ll a=f[i][j-1],b=f[i+(1<<j-1)][j-1];
			f[i][j]=(h[a]<h[b])?b:a;
		}
	for(ll i=1;i<=m;i++){
		scanf("%lld%lld",&ql[i],&qr[i]);
		ql[i]++;qr[i]++;ll x=RMQ(ql[i],qr[i]);
		q[x].push_back((node){qr[i],(x-ql[i]+1)*h[x],i});
	}
	solve(1,n);
	
	reverse(h+1,h+1+n);
	for(ll i=1;i<=n;i++)q[i].clear();
	for(ll j=1;(1<<j)<=n;j++)
		for(ll i=1;i+(1<<j)-1<=n;i++){
			ll a=f[i][j-1],b=f[i+(1<<j-1)][j-1];
			f[i][j]=(h[a]<h[b])?b:a;
		}
	for(ll i=1;i<=m;i++){
		ql[i]=n-ql[i]+1;qr[i]=n-qr[i]+1;
		swap(ql[i],qr[i]);ll x=RMQ(ql[i],qr[i]);
		q[x].push_back((node){qr[i],(x-ql[i]+1)*h[x],i});
	}
	T.Clear();solve(1,n);
	
	for(ll i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2022-04-08 19:26  QuantAsk  阅读(50)  评论(0编辑  收藏  举报