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;
}
辣鸡老年选手AFO在即