Description
Analysis
这个数据范围很显然的要分段程序
K=2
两段越平均越好,直接O(n)扫一遍即可
K=3
三段越平均越好,我们枚举其中一个等分点,调整另一个等分点(这个可以通过指针移动或二分来实现)。然后我们就有了两个等分点。但是这两个等分点的答案不一定是最优的。所以左边的等分点可能向右移一格。这会造成什么后果呢?右边的等分点会向右移动若干格。这个可以二分搞一波,然后整个过程就是O(nlogn)的
(我没打二分直接调整两个等分点向左右偏离一点点取min水过去了,面壁中)
K>3且N<=400
这个数据范围可以dp啦><
枚举圆环在哪个位置断开
状态很好设,f[i][j]表示前i个位置分了j段的最小值。
写出DP式子,就是
f[i][j]=min(f[k][j−1]+(s[i]−s[k]−avg)2)(k<i)
s表示前缀和
O(n3k) ,爆炸
一看,嘿嘿嘿,这不是斜率优化标准形式吗,化简一下式子,两个决策k,l,k优于l当且仅当
g(k,l)=F(k,j)−F(l,j)s[k]−s[l]<2∗s[i]
其中定义 F(i,j)=f[k][j−1]+s[k]2+2∗avg∗s[k]
i递增
然后就是裸的斜率优化了, O(n2k)
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(ll i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const ll N=600005,M=22;
const ll INF=9187201950435737471;
ll n,k,avg,tot,a[N],b[N],f[405][M],s[N],q[N],S[N];
ll sqr(ll x){return x*x;}
ll F(int i,int j)
{
return f[i][j]+sqr(s[i])+2*avg*s[i];
}
ll G(int k,int l,int j)
{
return F(k,j)-F(l,j);
}
ll H(int k,int l)
{
return 2*(s[k]-s[l]);
}
ll dp(ll st)
{
fo(i,st,st+n-1) b[i-st+1]=a[i];
fo(i,1,n) s[i]=s[i-1]+b[i];
memset(f,0,sizeof(f));
f[0][0]=0;
fo(i,1,n) f[i][1]=sqr(s[i]-avg);
fo(j,2,k)
{
int head=1,tail=1;
q[1]=j-1;
fo(i,j,n)
{
while(head<tail && G(q[head+1],q[head],j-1)<s[i]*H(q[head+1],q[head])) head++;
f[i][j]=f[q[head]][j-1]+sqr(s[i]-s[q[head]]-avg);
while(head<tail && G(i,q[tail],j-1)*H(q[tail],q[tail-1])<G(q[tail],q[tail-1],j-1)*H(i,q[tail])) tail--;
q[++tail]=i;
}
}
return f[n][k];
}
int main()
{
scanf("%lld %lld",&n,&k);
fo(i,1,n) scanf("%lld",&a[i]),a[i]*=k,tot+=a[i],a[n+i]=a[i],avg+=a[i];
fo(i,1,n+n) S[i]=S[i-1]+a[i];
avg/=k;
if(k==2)
{
ll ans=INF,sum=0,j=0;
fo(i,1,n)
{
sum-=a[i-1];
while(j<=i+n-1 && abs(sum-avg)>abs(sum+a[j+1]-avg)) sum+=a[++j];
ans=min(ans,sqr(sum-avg)+sqr(tot-sum-avg));
}
printf("%lld",ans);
return 0;
}
if(k==3)
{
ll ans=INF,sm=0,sm1=0,j=0,l=0;
fo(i,1,n)
{
sm-=a[i-1];
while(j<=i+n-1 && sm+a[j+1]<=avg) sm+=a[++j],sm1-=a[j];
if(j>l) l=j,sm1=0;
while(l<=i+n-3 && sm1+a[l+1]<=avg) sm1+=a[++l];
fo(p1,max(1ll,j-1),min(i+n-1,j+1))
fo(p2,max(p1+1,l-1),min(i+n-1,l+1))
{
ll x=S[p1]-S[i-1],y=S[p2]-S[p1];
ans=min(ans,sqr(x-avg)+sqr(y-avg)+sqr(tot-x-y-avg));
}
}
printf("%lld",ans);
return 0;
}
ll ans=INF;
fo(i,1,n) ans=min(ans,dp(i));
printf("%lld",ans);
return 0;
}