CF573E Bear and Bowling

(以下内容绝大部分参考集训队作业和这篇博客,建议阅读原文获得更好的体验)

本题可以考虑这样一个贪心策略:每次选取对答案贡献最大的点加入,直到最大贡献为负。

事实上这个策略是正确的,下面来证明。(下设第 \(i\) 个数当前的贡献为 \(w(i)\),选取集合 \(S\) 的答案为 \(val(S)\) )。

首先证明一个引理:

在上述策略下,如果 \(i\lt j,~ a_i\gt a_j\),那么 \(i\) 一定比 \(j\) 先选择(即 \(w(i)\gt w(j)\))。

证明:因为加入先后的差异仅在于中间已选元素对两端的贡献:中间 \(k\)\(j\) 的贡献为 \(a_j\),对 \(i\) 的贡献为 \(a_k\),我们只需证 \(\forall a_k,a_k\ge a_j\)。故我们考虑对 \([i,j]\) 中间的元素数量进行归纳证明:

  • 假设 \([i,j]\) 中有 \(0\) 个已选的元素,则显然先选择 \(i\)
  • 假设 \([i,j]\) 中有 \(x\) 个已选的元素,因为 \([i,k]\) 内选择元素数量显然严格小于 \(x\) ,所以 \([i,k]\) 满足引理,故 \(a_k\ge a_i\ge a_j\) ,引理得证。

有了这个引理之后,我们考虑反证这个策略:假设 \(val(A\cup\{x\}\cup C)\lt val(A\cup B)\)\(x\notin B,B\neq \empty\)\(x\) 为在策略下集合 \(A\) 下一个选择的元素,\(C\) 为任意集合):

  • 假设 \(B\) 中存在元素在 \(x\) 左边,设 \(y\)\(B\) 中在 \(x\) 左边最靠右的元素,由策略可得 \(w(x)\ge w(y)\) 。在 \(x\) 左边的元素对 \(x,y\) 的贡献为 \(a_x,a_y\) ,根据引理有 \(a_y\le a_x\);在 \(y\) 右边的元素贡献相同,\(x,y\) 中间没有元素。故选择 \(x\) 不会比选择 \(y\) 劣。
  • 假设 \(B\) 中所有元素在 \(x\) 右边,考虑最左边的元素 \(y\) ,其他元素对 \(x,y\) 贡献相同,故选择 \(x\) 不会比选择 \(y\) 劣。

综上,假设不成立,所以原策略是正确的。

之后,我们可以动态维护所有的 \(w(i)\),需要支持区间加一个数,区间加 \(a_i\),查询全局最大值。直接套用模板简单的数列题。复杂度可以做到根号。(没有代码 /kk)

通过这个结论可以得到一个重要推论:

\(S_i\) 为选择集合大小为 \(i\) 的一个最优解集合,则存在 \(S_i\sub S_{i+1}\)

这个推论可以帮助我们得到一个更优复杂度的做法。

我们考虑一个 \(n^2\) 的 DP:设 \(f_{i,j}\) 表示前 \(i\) 个元素,选了 \(j\) 个元素的答案:

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

由上面的推论可以得到:选择 \(a_i\)\(j\) 一定是一段后缀。

(事实上这个结论有一个牛逼的证明方法,可以看这篇博客。)

所以上面的 DP 是一个分段函数,我们可以用平衡树维护差分数组,每次平衡树上二分找到分界点插入,然后后缀加即可。

复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int gi()
{
	char c=getchar(); int x=0,f=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
	for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
	return x*f;
}
const int N=1e5+5;
int ch[N][2],sze[N],fa[N],n,rt,cnt;
ll val[N],tag[N],sum,ans;
void pushup(int x)
{
	sze[x]=sze[ch[x][0]]+sze[ch[x][1]]+1;
}
void pushdown(int x)
{
	if(!tag[x]||!x) return ;
	tag[ch[x][0]]+=tag[x],tag[ch[x][1]]+=tag[x];
	val[ch[x][0]]+=tag[x],val[ch[x][1]]+=tag[x];
	tag[x]=0;
}
void rotate(int x)
{
	int y=fa[x],z=fa[y];
	int k=ch[y][1]==x,w=ch[x][!k];
	ch[z][ch[z][1]==y]=x,ch[x][!k]=y,ch[y][k]=w;
	fa[w]=y,fa[y]=x,fa[x]=z;
	pushup(y);
}
void maintain(int x)
{
	if(fa[x]) maintain(fa[x]);
	pushdown(x); 
}
void splay(int x, int k)
{
	maintain(x);
	while(fa[x]!=k)
	{
		int y=fa[x],z=fa[y];
		if(z!=k) (ch[z][1]==y)^(ch[y][1]==x)?rotate(x):rotate(y);
		rotate(x);
	}
	pushup(x);
	if(!k) rt=x;
}
void insert(int& u, int f, int w, int rk)
{
	if(!u)
	{
		u=++cnt;
		sze[u]=1,fa[u]=f;
		val[u]=1ll*w*(rk+1);
		splay(u,0);
		return ;
	}
	pushdown(u);
	int now=rk+sze[ch[u][0]]+1;
	if(val[u]<1ll*w*now) insert(ch[u][0],u,w,rk);
	else insert(ch[u][1],u,w,now);
}
void dfs(int u)
{
	if(!u) return ;
	pushdown(u);
	dfs(ch[u][0]);
	sum+=val[u],ans=max(ans,sum);
	dfs(ch[u][1]);
}
int main()
{
	n=gi();
	for(int i=1;i<=n;++i)
	{
		int w=gi();
		insert(rt,0,w,0);
		val[ch[rt][1]]+=w,tag[ch[rt][1]]+=w;
	}
	dfs(rt),printf("%lld\n",ans);
}

posted @ 2020-04-19 01:02  x_faraway_x  阅读(203)  评论(1编辑  收藏  举报