Loading

题解「Luogu5665 划分」

转载注明来源:https://www.cnblogs.com/syc233/p/13663639.html


丧心病狂卡时空题


题意

给你一个长为 \(n\) 的数列 \(\{a_n\}\) ,需要找到若干个分界点 \(1 \leq k_1 <k_2 <k_3<\cdots<k_p < n\) ,满足:

\[\sum_{i=1}^{k_1} a_i \leq \sum_{i=k_1+1}^{k_2}a_i \leq \cdots\leq \sum_{i=k_p+1}^na_i \]

同时最小化

\[(\sum_{i=1}^{k_1} a_i)^2 + (\sum_{i=k_1+1}^{k_2}a_i)^2 + \cdots + (\sum_{i=k_p+1}^na_i)^2 \]


题解

容易想到一个 \(O(n^2)\) 的DP:令 \(f_i\) 表示划分 \(1 \sim i\) 的最小答案, \(g_i\) 表示答案取到 \(f_i\) 时上一段的末尾, \(s_i\)\(\{a_n\}\) 的前缀和。则有:

\[f_i=\min_{0\leq j <i}\{f_j+(s_i-s_j)^2|(s_j-s_{g_j})\leq(s_i-s_j)\} \]

更新 \(f_i\) 时顺带更新下 \(g_i\)

这样能拿到 \(64pt\)

注意到这样一个性质:最后一段的和越小,答案越优。

那么有:

\[\begin{aligned} g_i&=\max pos \ s.t. (s_i-s_{pos})\geq(s_{pos}-s_{g_{pos}})\\ &=\max pos \ s.t. s_i\geq(2 \cdot s_{pos}-s_{g_{pos}})\\ \end{aligned} \]

\({\rm{val}}(i)=2\cdot s_i-s_{g_i}\) ,则:

\[g_i=\max pos \ s.t. s_i\geq{\rm{val}}(pos) \]

因为 \(s_i\) 是递增的,所以若存在 \(a,b\) ,满足 \(a<b<i,{\rm{val}}(a)>{\rm{val}}(b)\) ,那么 \(a\) 能够转移时, \(b\) 一定能够转移,那 \(a\) 必定不会成为最优解。于是可以用单调队列来求出 \(g_i\)

由于这题时空都卡得比较死,所以最后用 \(g_i\) 来求出答案。


\(\text{Code}:\)

#include <cstring>
#include <cstdio>
#define maxn 40000005
#define maxm 100005
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;

template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	x*=f;
}

void write(__int128 x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}

int n,type,a[maxn],p[maxm],L[maxm],R[maxm];
lxl sum[maxn];
int q[maxn],l,r;

inline void make()
{
	int m;lxl x,y,z,bpp,bp,bn;
	lxl mod=1<<30;
	read(x),read(y),read(z),read(bpp),read(bp),read(m);
	for(int i=1;i<=m;++i) read(p[i]),read(L[i]),read(R[i]);
	for(int j=1;j<=m;++j)
	{
		for(int i=p[j-1]+1;i<=p[j];++i)
		{
			if(i==1) bn=bpp;
			else if(i==2) bn=bp;
			else bn=(x*bp+y*bpp+z)%mod;
			a[i]=bn%(R[j]-L[j]+1)+L[j];
			if(i>=3) bpp=bp,bp=bn;
		}
	}
}

int pre[maxn];

int main()
{
	// freopen("P5665.in","r",stdin);
	read(n),read(type);
	if(!type) for(int i=1;i<=n;++i) read(a[i]);
	else make();
	for(int i=1;i<=n;++i)
		sum[i]=sum[i-1]+a[i];
	l=1,r=0;
	for(int i=1;i<=n;++i)
	{
		while(l<=r&&2*sum[q[l]]-sum[pre[q[l]]]<=sum[i]) ++l;
		pre[i]=q[l-1];
		while(l<=r&&2*sum[q[r]]-sum[pre[q[r]]]>=2*sum[i]-sum[pre[i]]) --r;
		q[++r]=i;
	}
	__int128 ans=0;
	int now=n;
	while(now)
	{
		ans+=((__int128)sum[now]-sum[pre[now]])*(sum[now]-sum[pre[now]]);
		now=pre[now];
	}
	write(ans);
	return 0;
}

posted @ 2020-09-13 21:58  GoPoux  阅读(153)  评论(0编辑  收藏  举报