CF573E-Bear and Bowling【dp,平衡树】

正题

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


题目大意

给出一个长度为\(n\)的序列\(a\),求它的一个子序列\(b\),要求最大化

\[\sum_{i=1}^{|b|}b_i\times i \]

\(1\leq n\leq 10^5,|a_i|\leq 10^7\)


解题思路

首先我们考虑最暴力的\(dp\),设\(f_{i,j}\)表示到现在到\(a\)的第\(i\)个,然后选择了\(j\)个时的最大答案,那么我们有

\[f_{i,j}=max\{f_{i-1,j},f_{i-1,j-1}+b_i\times j\} \]

然后发现这个\(dp\)很难进行维护,我们尝试找下性质。
然后我没找到去看题解发现确实是性质题,对于一个\(i\),如果\(f_{i,j}\)\(f_{i,j-1}\)转移过来,那么\(f_{i,j+1}\)也一定是从\(f_{i-1,j}\)转移过来的。

证明的话可以看这篇大佬的博客:https://www.luogu.com.cn/blog/Mrsrz/solution-cf573e

所以我们可以用一个平衡树去维护每个\(i\)\(dp\)值,然后我们就只需要二分出两个转移方式的中转点\(k\),然后对于前面的我们不变,对于后面的我们在区间前插入一个\(f_{i,k-1}\),然后就是一个区间加等差序列的操作,用懒标记维护即可。

时间复杂度:\(O(n\log^2 n)\)

如果在平衡树上二分能做到\(O(n\log n)\)

当然还有另一种做法,考虑一个一个插入答案,能够证明不停插入会使得当前贡献最大的数也是最优的。

那么我们就只需要用分块维护每一个数的新贡献就好了。

时间复杂度:\(O(n\sqrt n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const ll N=1e5+10;
ll n,cnt,rt,t[N][2],dat[N],siz[N];
ll w[N],lazy[N],lazy2[N],ans;
ll Newp(ll val){
	w[++cnt]=val;dat[cnt]=rand();
	siz[cnt]=1;return cnt;
}
ll Cpy(ll x)
{return Newp(w[x]);}
void Update(ll x,ll val,ll dr){
	w[x]+=val*(siz[t[x][0]]+dr);
	lazy[x]+=val*dr;lazy2[x]+=val;
}
void PushDown(ll x){
	if(lazy[x]){
		if(t[x][0])w[t[x][0]]+=lazy[x],lazy[t[x][0]]+=lazy[x];
		if(t[x][1])w[t[x][1]]+=lazy[x],lazy[t[x][1]]+=lazy[x];
		lazy[x]=0;
	}
	if(lazy2[x]){
		if(t[x][0])Update(t[x][0],lazy2[x],0);
		if(t[x][1])Update(t[x][1],lazy2[x],siz[t[x][0]]+1);
		lazy2[x]=0;
	}
	return;
}
void PushUp(ll x){siz[x]=siz[t[x][0]]+siz[t[x][1]]+1;return;}
void Split(ll &x,ll &y,ll p,ll val){
	if(!p){x=y=0;return;}PushDown(p);
	if(siz[t[p][0]]<=val)x=p,Split(t[x][1],y,t[p][1],val-siz[t[p][0]]-1);
	else y=p,Split(x,t[y][0],t[p][0],val);
	PushUp(p);
}
ll Merge(ll x,ll y){
	if(!x||!y)return x|y;
	PushDown(x);PushDown(y);
	if(dat[x]<dat[y]){
		t[x][1]=Merge(t[x][1],y);
		PushUp(x);return x;
	}
	else{
		t[y][0]=Merge(x,t[y][0]);
		PushUp(y);return y;
	}
}
ll GetVal(ll &rt,ll pos){
	ll x,y,z;
	Split(x,z,rt,pos);
	Split(x,y,x,pos-1);
	ll ans=w[y];
	x=Merge(x,y);
	rt=Merge(x,z);
	return ans;
}
void GetAns(ll x){
	if(!x)return;PushDown(x);
	ans=max(ans,w[x]);
	GetAns(t[x][0]);GetAns(t[x][1]);
	return;
}
bool check(ll x,ll w){
	ll a=GetVal(rt,x);
	ll b=GetVal(rt,x-1);
	return a>b+x*w;
}
signed main()
{
	scanf("%lld",&n);rt=Newp(0);
	rt=Merge(rt,Newp(-1e18));
	for(ll i=1,k;i<=n;i++){
		scanf("%lld",&k);
		ll l=1,r=i;
		while(l<=r){
			ll mid=(l+r)>>1;
			if(check(mid,k))l=mid+1;
			else r=mid-1;
		}
		ll x,y,z,d;
		Split(x,z,rt,l-1);
		Split(z,d,z,n-1);
		Split(x,y,x,l-2);
		d=Cpy(y);
		z=Merge(d,z);
		Update(z,k,l);
		rt=x;rt=Merge(rt,y);
		rt=Merge(rt,z);
	}
	GetAns(rt);
	printf("%lld\n",ans);
	return 0;
}```
posted @ 2022-03-14 15:27  QuantAsk  阅读(27)  评论(0编辑  收藏  举报