Link
Diffculty
普通:算法难度6,思维难度6,代码难度5
凸优化:算法难度7,思维难度7,代码难度8
Description
你正在玩一个关于长度为 的非负整数序列的游戏。这个游戏中你需要把序列分成 个非空的块。为了得到 块,你需要重复下面的操作 次:
- 选择一个有超过一个元素的块(初始时你只有一块,即整个序列)
- 选择两个相邻元素把这个块从中间分开,得到两个非空的块。
每次操作后你将获得那两个新产生的块的元素和的乘积的分数。你想要最大化最后的总得分,输出一组方案。
Solution
首先可以证明答案只和切分点有关。
如果固定切分点之后,两两合并的答案一定是两两区间和的乘积之和。
那么我们可以设计出dp状态以及转移方程:
代表前 个位置,分了 块,并且强制最后一块以 结尾的最大价值。
我们令 表示 的前缀和。
则
这个方程也是非常明显的,自己推一下就知道了。
显然可以拆式子,发现这东西可以斜率优化:
要求最大化截距。
那么常规套路,直接记录 个上凸壳就好了,并且单调队列维护一波。
只要再记录一波方案,就没什么问题了。
斜率优化的部分非常简单,大家应该都会做。
然而你会发现这题还是可以凸优化,感性理解它的答案是个上凸函数。
凸优化求答案的话,只需要注意一下记录最小次数的部分就好了,还有要判分母为0或者直接判0。
总而言之,只求答案的话,套路还是没什么变化的。
你可以在bzoj测试自己凸优化的答案是否正确。
我们现在考虑如何输出方案。
我们考虑凸优化的时候,多记录一个最大次数,同时记录所有最优值的转移前驱。
我们考虑每个点的可行次数都是一个区间,那么只要符合当前点的区间,之后也一定存在符合的区间。
那么我们相当于记录了一个决策图,我们只要从 号点开始走,每次只走合法的,一定会走出一组合法方案的。
还要注意0对方案的影响,0会影响最大次数,也会影响方案输出,具体的数据你可以在UOJ看到。
我目前取得了BZOJ的rank3,洛谷的rank3,UOJ的rank2
爷稳稳tql
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#define LL long long
#define LD long double
using namespace std;
inline void read(int &x){
char ch=' ';
while(ch<'0' || ch>'9')ch=getchar();
while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
}
const int N=1e5+5,K=205;
vector<int> c[N];
int n,k,a[N];
LL sum[N];
int q[N],L,R;
LL dp[N],mn[N],mx[N],mn2[N],mx2[N];
inline bool check(int a,int b,int c){
LD k1=2e18,k2=2e18;
if(sum[b]!=sum[a])k1=(LD)(dp[b]-sum[b]*sum[b]-dp[a]+sum[a]*sum[a])/(LD)(sum[b]-sum[a]);
if(sum[c]!=sum[b])k2=(LD)(dp[c]-sum[c]*sum[c]-dp[b]+sum[b]*sum[b])/(LD)(sum[c]-sum[b]);
if(k1<k2)return 1;
else return 0;
}
inline bool check2(int a,int b,LL v){
LD k1=(LD)(dp[b]-sum[b]*sum[b]-dp[a]+sum[a]*sum[a])/(LD)(sum[b]-sum[a]);
if(k1>=v){
if(fabs(k1-v)<1e-8)
mn2[b]=min(mn2[a],mn2[b]),mx2[b]=max(mx2[a],mx2[b]);
return 1;
}
else return 0;
}
inline void print(int pos,int times,int flag){
while(pos && !a[pos] && mx[pos-1]>=times)pos--;
if(!pos)
return;
times--;
for(vector<int>::iterator it=c[pos].begin();it!=c[pos].end();++it){
int x=*it;
if(mn[x]<=times && times<=mx[x]){
print(x,times,1);
break;
}
}
if(flag)printf("%d ",pos);
}
int main(){
read(n);read(k);++k;
for(int i=1;i<=n;++i)read(a[i]);
for(int i=1;i<=n;++i)sum[i]=sum[i-1]+a[i];
LL l=0,r=sum[n]*sum[n],mid;
while(l<r){
mid=(l+r)>>1;
L=1;R=0;
for(int i=1;i<=n;++i){
if(L>R){
if(!a[i]){
dp[i]=dp[i-1];
mn[i]=mn[i-1];
mn2[i]=mn[i];
}
else{
dp[i]=-mid;
mn[i]=1;
mn2[i]=mn[i];
while(L<R && check(q[R-1],q[R],i))R--;
q[++R]=i;
}
}
else{
if(!a[i]){
dp[i]=dp[i-1];
mn[i]=mn[i-1];
mn2[i]=mn[i];
}
else{
mn2[q[L]]=mn[q[L]];
while(L<R && check2(q[L],q[L+1],-sum[i]))L++;
dp[i]=dp[q[L]]+sum[q[L]]*(sum[i]-sum[q[L]])-mid;
mn[i]=mn2[q[L]]+1;
mn2[i]=mn[i];
if(-mid>=dp[i]){
dp[i]=-mid;
mn[i]=mn2[i]=1;
}
while(L<R && check(q[R-1],q[R],i))R--;
q[++R]=i;
}
}
}
if(mn[n]>k)l=mid+1;
else r=mid;
}
mid=l;
L=1;R=0;
for(int i=1;i<=n;++i){
if(L>R){
if(!a[i]){
dp[i]=dp[i-1];
mn[i]=mn[i-1];
mn2[i]=mn[i];
if(!mid)c[i].push_back(i-1),mx[i]=mx[i-1]+1;
else mx[i]=mx[i-1];
mx2[i]=mx[i];
while(L<R && check(q[R-1],q[R],i))R--;
q[++R]=i;
}
else{
c[i].push_back(0);
dp[i]=-mid;
mn[i]=1;
mn2[i]=mn[i];
mx[i]=1;
mx2[i]=mx[i];
while(L<R && check(q[R-1],q[R],i))R--;
q[++R]=i;
}
}
else{
if(!a[i]){
dp[i]=dp[i-1];
mn[i]=mn[i-1];
mn2[i]=mn[i];
if(!mid)c[i].push_back(i-1),mx[i]=mx[i-1]+1;
else mx[i]=mx[i-1];
mx2[i]=mx[i];
while(L<R && check(q[R-1],q[R],i))R--;
q[++R]=i;
}
else{
mn2[q[L]]=mn[q[L]];
mx2[q[L]]=mx[q[L]];
while(L<R){
int a=q[L],b=q[L+1];
LD k1;
if(sum[b]!=sum[a])k1=(LD)(dp[b]-sum[b]*sum[b]-dp[a]+sum[a]*sum[a])/(LD)(sum[b]-sum[a]);
else k1=2e18;
if(k1>-sum[i]){
c[i].clear();
++L;
}
else if(k1==-sum[i]){
mn2[b]=min(mn2[a],mn2[b]),mx2[b]=max(mx2[a],mx2[b]);
c[i].push_back(a);
++L;
}
else break;
}
c[i].push_back(q[L]);
dp[i]=dp[q[L]]+sum[q[L]]*(sum[i]-sum[q[L]])-mid;
mn[i]=mn2[q[L]]+1;
mx[i]=mx2[q[L]]+1;
mn2[i]=mn[i];
mx2[i]=mx[i];
if(-mid>dp[i]){
dp[i]=-mid;
mx2[i]=mx[i]=mn[i]=mn2[i]=1;
c[i].clear();
c[i].push_back(0);
}
else if(-mid==dp[i])mn[i]=mn2[i]=1,c[i].push_back(0);
while(L<R && check(q[R-1],q[R],i))R--;
q[++R]=i;
}
}
}
printf("%lld\n",(long long)(dp[n]+k*mid));
print(n,k,0);
return 0;
}