把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF573E】Bear and Bowling(分块维护凸壳)

点此看题面

  • 给定一个序列\(a_{1\sim n}\),要求从中选出一个子序列\(b_{1\sim m}\),最大化\(\sum_{i=1}^mi\times b_i\)
  • \(n\le10^5,|a_i|\le10^7\)

贪心

一个贪心,就是我们每次选取贡献最大的位置尝试加入。

这个贪心正确性显然,因此问题就在于怎么表示贡献并求出最大值。

分块维护凸壳

这里的贡献要分两类讨论,设其为\(a_i\times t+b\),其中\(t\)为之前选中的数个数\(+1\)\(b\)为之后所有选中的数之和。

选取了一个数,相当于要给之前全部的\(b\)加上\(a_i\),给之后所有的\(t\)加上\(1\)

一般的数据结构似乎都不擅长维护这种东西,发现这里的\(t\)是单调递增的,因此考虑分块,并对每个块维护一个凸壳搞斜率优化。

对于一个块,我们先将所有元素按照斜率\(a_i\)排个序,然后维护一个凸壳。

要给一个块\(b\)全部加上\(a_i\),显然不会对块内元素之间的相对大小关系造成任何影响,只要打标记即可。

要给一个块\(t\)全部加上\(1\),同样打上一个标记,然后在询问一个块的答案时,首先把开头不优的元素弹出,再返回开头元素(经典斜率优化操作)。

至于最优元素所在块,我们只需把它的贡献修改为\(-INF\),暴力改完前后的贡献,然后重构这个块的凸壳即可。

代码:\(O(n\sqrt n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define BT 320
#define BS 320
#define LL long long
#define Pr pair<LL,int>
using namespace std;
int n,sz,a[N+5],bl[N+5];LL f[N+5];I bool cmp(CI x,CI y) {return a[x]<a[y];}
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define D isdigit(oc=tc())
	int Ff;char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0,Ff=1;W(!D) Ff=oc^'-'?1:-1;W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));x*=Ff;}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
struct Block
{
	#define V(x) (f[x]+a[x]*t+b)//算上标记计算实际值
	int H,T,q[BS+5],p[BS+5];LL t,b;
	I void Build(CI L,CI R)//建块
	{
		RI i;for(i=L;i<=R;++i) f[p[i-L+1]=i]+=a[i]*t+b;t=b=0;sort(p+1,p+R-L+2,cmp);//按斜率排序
		#define S(x,y) (a[x]^a[y]?1.0*(f[y]-f[x])/(a[y]-a[x]):(f[y]>f[x]?1e18:-1e18))
		for(H=1,T=0,i=1;i<=R-L+1;q[++T]=p[i++]) W(H<T&&S(q[T-1],q[T])<S(q[T],p[i])) --T;//维护凸壳
	}
	I Pr Q() {W(H<T&&V(q[H])<V(q[H+1])) ++H;return make_pair(V(q[H]),q[H]);}//弹去队首不优元素
}B[BT+5];
int main()
{
	RI i,x;for(read(n),sz=sqrt(n),i=1;i<=n;++i) read(a[i]),bl[i]=(i-1)/sz+1,f[i]=a[i];
	for(i=1;i<=bl[n];++i) B[i].Build((i-1)*sz+1,min(i*sz,n));//初始建块
	Pr k;LL ans=0;W(true)
	{
		for(k=make_pair(0LL,0),i=1;i<=bl[n];++i) k=max(k,B[i].Q());if(!k.first) break;ans+=k.first,x=k.second;//直至没有贡献
		for(i=1;i^bl[x];++i) B[i].b+=a[x];for(i=bl[x]+1;i<=bl[n];++i) ++B[i].t;//更新前后块
		for(i=(bl[x]-1)*sz+1;i^x;++i) f[i]+=a[x];for(i=x+1;i<=min(bl[x]*sz,n);++i) f[i]+=a[i];//更新当前块前后元素
		f[x]=-1e18,B[bl[x]].Build((bl[x]-1)*sz+1,min(bl[x]*sz,n));//把当前位置贡献改为-INF,重构当前块
	}return printf("%lld\n",ans),0;
}
posted @ 2021-04-06 11:36  TheLostWeak  阅读(63)  评论(0编辑  收藏  举报