CSP 2019 Day2 T2 划分

\(f_{i,j}\)表示只考虑前\(i\)个数,最后一段的开始端点的前一个位置为\(j\)时的最优解
那么有转移方程

\[f_{i,j}=(sum_i-sum_j)^2+min(f_{j,k})\ \ 其中sum_j-sum_k\le sum_i-sum_j \]

其中\(sum_i\)表示前缀和
显然直接转移是\(O(n^3)\) 可以获得\(36\)分的高分
但是可以发现当\(j\)固定时,\(i\)向右移动满足条件最小的\(k\)一定不会向右移动,满足单调性
只用维护\(f_{j,k}\)的最小值就可以了
时间复杂度为\(O(n^2)\)可通过\(64\)
观察这样一个东西:(盗别人的图,勿喷)

假设\(sumL+x\le sumR\)
如果\(x\)放到\(sumL\)中,答案是\(ans_1=(sumL+x)^2+sumR^2=sumL^2+sumR^2+x^2+2sumL*x\)
如果\(x\)放到\(sumR\)中,答案是\(ans_2=(sumR+x)^2+sumL^2=sumL^2+sumR^2+x^2+2sumR*x\)
显然\(sumL\le sumR\),因此\(ans_1\le ans_2\)
即如果都合法,那么最后一段的长度越小越好
于是我们设\(pre_i\)表示只考虑前\(i\)个时,最优解时最后一段的开始端点的前一个位置(如果有多个位置则尽量靠后)
若当前枚举到\(i\),考虑转移点\(j\)
即要满足\(sum_i-sum_j\ge sum_j-sum_{pre_j}\)
也就是\(sum_i\ge 2sum_j-sum_{pre_j}\)
\(val_j=2sum_j-sum_{pre_j}\),那么\(sum_i\ge val_j\)
我们要在满足这个限制的\(j\)中找到最大的(因为要让最后一段尽量小)
这个东西显然可以用单调队列进行优化
具体来说,每次求\(pre_i\)时,先把队首的所有过时决策剔除(如果下一个元素都满足\(val_j\le sum_i\),那么就弹出队首,因为下一个一定比当前队首的更优)
然后直接用队首来更新
求出\(pre_i\)后加入队尾
对于\(pre\ge pre_i\),全部都要弹出(因为当前值比它小还比它靠后,那么它就没有机会更新其他答案了)
最后再从\(n\)开始往回走统计答案即可

time complexity:

\(O(n)\)

code:

#include<bits/stdc++.h>
using namespace std;
const int N=4e7+5,mod=1<<30;
typedef long long ll;
int n,type,a[N],pre[N],q[N],l,r,x,y,z,b[N],m;
ll s[N];
inline ll dr(int x){return 2*s[x]-s[pre[x]];}
inline int add(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
void write(__int128 x){if(x>9)write(x/10);putchar(x%10+'0');}
int main()
{
	scanf("%d%d",&n,&type);
	if(type)
	{
		scanf("%d%d%d%d%d%d",&x,&y,&z,b+1,b+2,&m);
		for(int i=3;i<=n;++i)b[i]=add(z,add(mul(x,b[i-1]),mul(y,b[i-2])));
		int la=0;
		for(int i=1;i<=m;++i)
		{
			int p,l,r;scanf("%d%d%d",&p,&l,&r);
			for(int j=la+1;j<=p;++j)a[j]=b[j]%(r-l+1)+l,s[j]=s[j-1]+1ll*a[j];
			la=p;
		}
	}
	else for(int i=1;i<=n;++i)scanf("%d",a+i),s[i]=s[i-1]+1ll*a[i];
	l=1,r=0;
	for(int i=1;i<=n;++i)
	{
		while(l+1<=r&&dr(q[l+1])<=s[i])++l;
		if(dr(q[l])<=s[i])pre[i]=q[l];
		while(l<=r&&dr(i)<=dr(q[r]))--r;
		q[++r]=i;
	}
	int now=n;__int128 ans=0;
	while(now)ans+=(__int128)(s[now]-s[pre[now]])*(s[now]-s[pre[now]]),now=pre[now];
	write(ans);
	return 0;
}
posted @ 2020-11-03 12:00  BILL666  阅读(96)  评论(0编辑  收藏  举报