P4983忘情

今天挺开心的\(\sim\),省选加油\(!\)

\(P4893\)忘情

我能说今晚我才真正学会\(wqs\)和斜率优化吗\(?\)

恰好选几个,必然需要\(wqs\)二分一下

那么考虑不考虑次数情况下转移,\(dp[i]\)表示前\(i\)个分成若干段的最小代价,然后直接转移是\(O(n^2)\)

比较显然的可以写成斜率式

\(dp[j]-2\times sum[j]+sum[j]\times sum[j]=2\times sum[i]\times sum[j]+dp[i]-(sum[i]+1)^2\)

维护一个下凸包即可

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define int long long
#define MAXN 100005
using namespace std;
int a[MAXN],sum[MAXN],dp[MAXN];
int sta[MAXN],num[MAXN];
int n,m,INF=LLONG_MAX;
int Y(int x)
{
	return dp[x]-2*sum[x]+sum[x]*sum[x];
}
int X(int x)
{
	return sum[x];
}
double slope(int x1,int x2)
{
	   return (double)(Y(x2)-Y(x1))/(X(x2)-X(x1));
}
bool check(int x)
{
	 int head,tail;
	 sta[head=tail=1]=0;
	 memset(num,0,sizeof(num));
	 memset(dp,0x3f,sizeof(dp));
     dp[0]=0;
	 for(int i=1;i<=n;i++)
	 {
	 	 while(head<tail&&slope(sta[head],sta[head+1])<2*sum[i]) head++;
	 	 dp[i]=dp[sta[head]]+(sum[i]-sum[sta[head]]+1)*(sum[i]-sum[sta[head]]+1)+x;
	 	 num[i]=num[sta[head]]+1;
	 	 while(head<tail&&slope(sta[tail-1],sta[tail])>slope(sta[tail],i)) tail--;
	 	 sta[++tail]=i;
	 }
	 return num[n]<=m;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	    sum[i]=sum[i-1]+a[i];
	}
	if(m==2)
	{
	   int Ans=INF;
	   for(int i=0;i<=n;i++)
	   {
	   	   Ans=min(Ans,(sum[i]+1)*(sum[i]+1)+(sum[n]-sum[i]+1)*(sum[n]-sum[i]+1));
	   }
	   cout<<Ans<<"\n";
	   return 0;
	}
	memset(dp,0x3f,sizeof(dp));
    int l=0,r=INF/2,mid,ans;
    while(l<=r)
    {
    	 mid=(l+r)>>1;
    	 if(check(mid))
    	 {
    	 	//如果小于等于m
			//那么就是减多了
			r=mid-1;
			ans=mid;
		 }
		 else l=mid+1;
	}
//	cout<<"Ans: "<<ans<<"\n";
	check(ans);
	cout<<dp[n]-m*ans<<"\n";	
}
posted @ 2022-04-07 20:22  Authentic_k  阅读(42)  评论(0编辑  收藏  举报