CF573E Bear and Bowling(分块+斜率优化)

洛谷题目传送门

有一个贪心策略是维护下一个数字选择某一个位置的最优解,然和每次选择最优的来转移,把他选了,然后更新其他位置的代价
t i t_i ti表示 i i i之前选的数字的个数, b i b_i bi表示 i i i之后选的数字的 a a a值之和
那么在 i i i选择的代价是 a i × t i + b i a_i\times t_i+b_i ai×ti+bi,我们就是要最大化这个式子
考虑用分块维护这个东西,然后在每个块内建立一个凸包,然后就是斜率优化的套路
考虑设 f i = a i × t i + b i f_i=a_i\times t_i+b_i fi=ai×ti+bi,则 b i = − a i × t i + f i b_i=-a_i\times t_i+f_i bi=ai×ti+fi
相当于是用一条斜率为 t i t_i ti的线,截若干形容 ( − a i , b i ) (-a_i,b_i) (ai,bi)的点
为了方便维护,我们让整个块的 t i t_i ti相同,然后随着选的数的增加, t t t的值会增加,所以最优价的位置不会向前移动
当选出一个数是x时,之前块的所有元素的 b i b_i bi都会加上 a x a_x ax,之后所有块的 t i t_i ti都会加1
因为是整块加减,所以可以打标记,因为整体加相当于将所有点平移,而斜率不会发生改变
所以不会影响斜率的查询
至于 x x x这个块,因为不是整体平移,斜率会改变,所以把整个块重构即可
总复杂度 O ( n n ) O(n\sqrt n) O(nn )
略微卡常

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 1e15;
typedef double db;
const LL N = 2e5+7;
const LL Q = 500;
LL que[Q][Q],Head[Q],Tail[Q],St[Q],Ed[Q],Pos[N];
LL A[N],B[N],t[Q],Add[Q];
LL p[N];
LL len,cnt=0;
LL n;
LL cmp(LL a,LL b)
{
	return A[a]<A[b];
}
db slope(LL x,LL y)
{
	if(A[y]==A[x])
	{
		if(B[x]==B[y]) return 1;
		if(B[x]<B[y]) return INF;
		return -INF;
	}
	return (B[y]-B[x])/(A[y]-A[x]);
}

void Build(LL x)
{
	Head[x]=1;
	Tail[x]=0;
	LL l=St[x],r=Ed[x];
	for(LL o=l;o<=r;o++)
	{
		LL i=p[o];
		B[i]=B[i]+Add[x]+t[x]*A[i];
	}
	Add[x]=0;
	t[x]=0;
	for(LL o=l;o<=r;o++)
	{
		LL i=p[o];
		while(Head[x]<Tail[x]&&slope(que[x][Tail[x]-1],que[x][Tail[x]])<=slope(que[x][Tail[x]],i)) Tail[x]--;
		que[x][++Tail[x]]=i;
	}
//	cout<<x<<": "<<endl;
//	for(LL i=Head[x];i<=Tail[x];i++)
//	cout<<que[x][i]<<'-'<<A[que[x][i]]<<'-'<<B[que[x][i]]<<' ';
//	cout<<endl;
//	cout<<"---------"<<endl;
}
pair<LL,LL> Ask(LL x)
{
	while(Head[x]<Tail[x]&&slope(que[x][Head[x]],que[x][Head[x]+1])>=-t[x]) ++Head[x];
	if(Head[x]>Tail[x]) return make_pair(-INF,0);
//	cout<<que[x][Head[x]]<<' '<<Add[x]<<' '<<t[x]<<' '<<A[que[x][Head[x]]]<<endl;
	return make_pair(B[que[x][Head[x]]]+Add[x]+t[x]*A[que[x][Head[x]]],que[x][Head[x]]);
}
inline LL read()
{
    LL x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int main()
{
//	freopen("JOI.in","r",stdin);
//	freopen("JOI.out","w",stdout);
	scanf("%lld",&n);
	len=sqrt(n);
	if(len==0) len=1;
	if(n==1) len=1;
	for(LL i=1;i<=n;i++)
	{
		A[i]=read();
		B[i]=A[i];
		p[i]=i;
		Pos[i]=(i-1)/len+1;
		if(!St[Pos[i]]) St[Pos[i]]=i;
		Ed[Pos[i]]=max(Ed[Pos[i]],i);
		cnt=max(cnt,Pos[i]);
	}
	if(n==1)
	{
		cout<<max(0ll,A[1]);
		return 0;
	}
	for(LL i=1;i<=cnt;i++)
	{
		sort(p+St[i],p+Ed[i]+1,cmp);
		Build(i);
	}
	LL Ans=0,sum=0;
	for(LL i=1;i<=n;i++)
	{
		pair<LL,LL> ans(-INF,0);
		for(LL x=1;x<=cnt;x++)
		{
			pair<LL,LL> pos=Ask(x);
			if(pos.first>=ans.first) ans=pos;
		}
//		cout<<ans.first<<' '<<ans.second<<endl;
		sum+=ans.first;
		Ans=max(Ans,sum);
		LL r=ans.second,R=Pos[r];
		for(LL x=1;x<R;x++) Add[x]+=A[r];
		for(LL x=R+1;x<=cnt;x++) t[x]++;
		for(LL x=St[R];x<r;x++) B[x]+=A[r];
		for(LL x=r+1;x<=Ed[R];x++) B[x]+=A[x];
		B[r]=-INF;
		Build(R); 
	}
	cout<<Ans;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值