Luogu P5665 划分

MD今天和陈指导一起看了下觉得真是血妈简单,不过考场上还要写高精我估计就直接放弃了,现在肯定直接用__int128水了

(f_{i,j})表示上一次取的点是(i),其对应的决策点为(j)的答案,显然(f_{i,j}=min { f_{j,k}+operatorname{sum}(j,i)})

很显然我们可以把(f_{i,j})看作一个(f_i),设其最优决策点为(pos_i),结合柯西不等式(a^2+b^2<(a+b)^2)我们容易发现这个转移具有决策单调性(猜都猜出来了),每个位置最右边的决策点是最优的

考虑用单调队列维护最右边的前驱,记(pfx_i)(sum_{j=1}^i a_j),若(pfx_i-pfx_{q_{H+1}}ge pfx_{q_{H+1}}-pfx_{pos_{q_{H+1}}})就可以移动前驱,移下项我们发现只要维护(2 imes pfx_i-pfx_{pos_i})单调递增即可

#include<bits/stdc++.h>
#define RI register int
#define CI const int&
#define calc(x) (2LL*pfx[x]-pfx[pos[x]])
using namespace std;
const int N=4e7+5,M=1e5+5;
int n,tp,m,x,y,z,b[N],l[M],r[M],p[M],q[N],pos[N]; long long pfx[N];
inline void write(__int128 x)
{
	if (x>9) write(x/10); putchar(x%10+'0');
}
int main()
{
	RI i,j; scanf("%d%d",&n,&tp); if (!tp)
	for (i=1;i<=n;++i) scanf("%d",&x),pfx[i]=pfx[i-1]+x; else
	{
		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=1;i<=m;++i) scanf("%d%d%d",&p[i],&l[i],&r[i]);
		for (i=j=1;j<=m;++j) while (i<=p[j])
		pfx[i]=pfx[i-1]+b[i]%(r[j]-l[j]+1)+l[j],++i;
	}
	RI H=1,T=1; for (i=1;i<=n;++i)
	{
		while (H<T&&calc(q[H+1])<=pfx[i]) ++H; pos[i]=q[H];
		while (H<T&&calc(q[T])>calc(i)) --T; q[++T]=i;
	}
	__int128 ans=0; for (i=n;i;i=pos[i])
	ans+=(__int128)(pfx[i]-pfx[pos[i]])*(pfx[i]-pfx[pos[i]]);
	return write(ans),0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/13928664.html