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);
}