把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5665】划分(单调队列优化DP)

点此看题面

  • 给定\(n\)个数\(a_{1\sim n}\),要求找到若干分界点\(k_{1\sim p}\)满足\(\sum_{i=1}^{k_1}a_i\le\sum_{i=k_1+1}^{k_2}a_i\le\cdots\le\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+...+(\sum_{i=k_p+1}^na_i)^2\)最小值。
  • \(n\le4\times10^7\)

单调性

感觉这个东西一看就很有单调性。(然而去年的我在考场上并没能想起来。。。)

不严谨地证一下,假设我们现在有三个块\(A,B,C\),有三种可能:

  • \(A,B,C\)独自成块:\(A^2+B^2+C^2\)
  • \(A\)\(B\)合并:\(A^2+B^2+C^2+2AB\)
  • \(B\)\(C\)合并:\(A^2+B^2+C^2+2AC\)

显然这三种可能性代价依次递增。

也就是说,我们的决策点能右移一定右移。

单调队列

\(i\)能从\(j\)转移的条件是\(s_i-s_j\ge s_j-s_{g_j}\),其中\(g_j\)\(j\)的决策点。

移下项令其分为只与\(i\)有关和只与\(j\)有关两部分,就是\(s_i\ge 2s_j-s_{g_j}\)

那么我们只要开一个单调队列,维护\(2s_j-s_{g_j}\)递增即可。

高精?

又不是真的在考场上。

懒了点直接__in128了。

代码:\(O(n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 40000000
using namespace std;
int n,ty,a[N+5],q[N+5],g[N+5];long long s[N+5];
namespace Data//大数据生成
{
	#define M 100000
	int x,y,z,m,b[N+5];I void Work()
	{
		RI i,j,p,l,r;scanf("%d%d%d%d%d%d",&x,&y,&z,b+1,b+2,&m);
		for(i=3;i<=n;++i) b[i]=(1LL*x*b[i-1]+1LL*y*b[i-2]+z)&((1<<30)-1);
		for(i=j=1;i<=m;++i) {scanf("%d%d%d",&p,&l,&r);W(j<=p) a[j]=(b[j]%(r-l+1))+l,++j;}//甚至造数据都要写个双指针。。。
	}
}
I void write(__int128 x) {putchar(x?(x>9&&(write(x/10),0),x%10+48):48);}//__int128需要输优
int main()
{
	RI i;if(scanf("%d%d",&n,&ty),!ty) for(i=1;i<=n;++i) scanf("%lld",a+i);else Data::Work();//读入数据
	RI H=1,T=1;for(i=1;i<=n;++i) s[i]=s[i-1]+a[i];for(q[1]=0,i=1;i<=n;++i)
	{
		#define Calc(j) (2*s[j]-s[g[j]])//从j转移
		W(H^T&&Calc(q[H+1])<=s[i]) ++H;g[i]=q[H];W(H^T&&Calc(q[T])>=Calc(i)) --T;q[++T]=i;//每次取队首作为决策点,维护Calc()单调递增
	}
	__int128 t=0;for(i=n;i;i=g[i]) t+=(__int128)(s[i]-s[g[i]])*(s[i]-s[g[i]]);return write(t),0;//计算答案
}
posted @ 2020-11-04 21:14  TheLostWeak  阅读(74)  评论(0编辑  收藏  举报