CF573E Bear and Bowling

一、题目

点此看题

二、解法

首先有一个根本想不到比较显然的贪心:直接贪心选取贡献最大的 \(i\)

所以做题策略还是要调整啊,大胆猜结论,小心验证,如果能拍上几万组肯定就没问题

那么每个 \(i\) 的贡献可以写成 \(k_ia_i+b_i\) 的形式,其中 \(k_i\) 表示前面已经选取的点数\(+1\)\(b_i\) 表示后面选取的点权值和,我们的任务就是动态维护这 \(n\) 个一次函数的最大值。

考虑修改的方式,选取一个点之后把一段前缀的 \(b_i\) 加上某个值,把一段后缀的 \(k_i\)\(1\)

可以考虑用凸包维护,这相当于有一条斜率为 \(-k\) 的直线来截取若干个 \((a_i,b_i)\) 的点所得到的最大截距,所以我们维护上凸包,因为要支持奇怪的修改所以我们在外面套一个分块。

对于每个块分别求凸包,如果是整体修改 \(b\) 那么最优决策点不变,如果是整体修改 \(k\) 那么最优决策点会往右移动,因为最优决策点是单调的,所以可以那一个指针去指一下最优决策点即可。

需要预先对块内 \(a\) 排序,每次要重构被取点的凸包,时间复杂度 \(O(n\sqrt n)\)

三、解法

一次函数想凸包,奇怪修改用分块。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std; 
const int M = 100005;
const int N = 405;
#define int long long
#define pii pair<int,int>
#define mp make_pair
const int inf = 1e18;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ans,a[M],b[M],c[M],fk[N],fb[N],L[N],R[N];
int id[M],tp[N],nw[N],s[N][N];
int cmp(int x,int y)
{
	return a[x]<a[y];
}
double sp(int x,int y)
{
	if(a[x]==a[y]) return b[x]>b[y]?-inf:inf;
	return (b[x]-b[y])/(a[x]-a[y]);
}
int cal(int x)
{
	return fk[c[x]]*a[x]+b[x]+fb[c[x]];
}
void build(int w)//build the convex of block w
{
	//release the tag
	for(int i=L[w];i<=R[w];i++)
		b[i]+=fb[w]+fk[w]*a[i];
	fb[w]=fk[w]=tp[w]=0;nw[w]=1;
	for(int i=L[w];i<=R[w];i++)
	{
		while(tp[w]>1 && sp(s[w][tp[w]-1],s[w][tp[w]])
		<=sp(s[w][tp[w]],id[i])) tp[w]--;
		s[w][++tp[w]]=id[i];
	}
}
pii ask(int w)
{
	while(nw[w]<tp[w] && cal(s[w][nw[w]])
	<=cal(s[w][nw[w]+1])) nw[w]++;
	return mp(cal(s[w][nw[w]]),s[w][nw[w]]);
}
signed main()
{
	n=read();m=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		a[i]=read();id[i]=i;
		c[i]=(i-1)/m+1;
		if(!L[c[i]]) L[c[i]]=i;
		R[c[i]]=i;
	}
	for(int w=1;w<=c[n];w++)
	{
		sort(id+L[w],id+R[w]+1,cmp);
		fk[w]=1;build(w);
	}
	for(int i=1;i<=n;i++)
	{
		pii mx=mp(0,0);
		for(int w=1;w<=c[n];w++)
			mx=max(mx,ask(w));
		if(mx.first<=0) break;
		ans+=mx.first;
		int p=mx.second;
		for(int w=1;w<c[p];w++) fb[w]+=a[p];
		for(int j=L[c[p]];j<p;j++) b[j]+=a[p];
		for(int j=p+1;j<=R[c[p]];j++) b[j]+=a[j];
		for(int w=c[p]+1;w<=c[n];w++) fk[w]++;
		b[p]=-inf;build(c[p]);
	}
	printf("%lld\n",ans);
}
posted @ 2021-10-21 11:26  C202044zxy  阅读(70)  评论(0编辑  收藏  举报