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\times 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;
}
posted @ 2020-11-04 21:15  空気力学の詩  阅读(134)  评论(0编辑  收藏  举报