P5665 划分
我爱卡常,卡常爱我
题目大意
给你 \(n\) 个数,需要找到一些分界点 \(1 \leq k_1 \lt k_2 \lt \cdots \lt k_p \lt 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}^{n} a_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}^{n} a_i)^2
\]
题解
\(part~1:O(n^3)\)
我们可以定义 \(f_{i,j}\) 表示到第 \(i\) 个数,同时通过第 \(j\) 的点转移过来的最小值,我们可以易得状态转移方程:
\[\begin{array}{c}
f_{i,j}=min_{k=0}^{j-1}(f_{j,k}+(sum_i-sum_j)^2)\\
s.t.~sum_j-sum_k\le sum_i-sum_j
\end{array}
\]
由于我们是需要枚举 \(i,j,k\) 的,所以复杂度是 \(O(n^3)\) 的。
预计得分:\(36pts\)。
\(Part~2:O(n^2)\)
我们可以发现,对于任意一个位置,最优解是满足最后一组最小的,所以我们可以记录对于每一个位置最后一组的转移点 \(lst_i\) ,那么转移方程就如下:
\[\begin{array}{c}
f_i=min_{j=0}^{i-1}(f_j+(sum_i-sum_j)^2)\\
s.t.~sum_j-sum_{lst_j}\le sum_i-sum_j
\end{array}
\]
预计得分:\(64pts\)
\(Part~3:O(n)\)
对于上面 \(dp\) 方程的转移条件:
\[sum_j-sum_{lst_j}\le sum_i-sum_j
\]
我们来移一下项:
\[sum_i\ge 2sum_j-sum_{lst_j}
\]
又因为 \(sum\) 数组是满足单调递增的,而且我们要找到最大的 \(sum_j\) 来使得 \(sum_i-sum_j\) 也就是最后一段最小,可以考虑单调队列优化,复杂度就为 \(O(n)\) 了。
预计得分:\(88pts\)
\(Part~4:\) 神仙卡常
观察数据范围,我们发现肯定是要写高精的,然后我们发现恶心的出题人,非常善意的卡了你的空间又卡了你的时间。
能开的符合题目条件的 \(4\times 10^7\) 的数组你最多只能开两个,剩下的必须滚动或者直接一边算一边用。
等你解决了 \(MLE\) 后,你会发现,如果你写的是高精,你会被卡常。然后我就毅然决然的压了 \(10^8\) 的位,同时加上了火车头,爱了爱了。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=4e7+5,M=1e5+5,MOD=(1<<30),EACH=1e9,LOG=5;
struct lll
{
int s[LOG];
int l;
void print() const
{
if(l==0)
{
printf("0");
return ;
}
for(int i=l;i>=1;--i)
{
printf("%lld",s[i]);
}
return ;
}
void operator = (const int &x)
{
memset(s,0,sizeof(s));
l=0;
int xx=x;
while(xx>0)
l++,xx/=EACH;
xx=x;
for(int i=1;i<=l;++i)
s[i]=xx%EACH,xx/=EACH;
return ;
}
};
lll ans;
lll &operator + (const lll &a,const lll &x)
{
ans=0;
int jinwei=0;
ans.l=max(a.l,x.l);
for(int i=1;i<=ans.l;++i)
{
ans.s[i]=a.s[i]+x.s[i]+jinwei;
jinwei=0;
if(ans.s[i]>=EACH)
{
jinwei=ans.s[i]/EACH;
ans.s[i]%=EACH;
if(i==ans.l)
++ans.l;
}
}
return ans;
}
lll &operator * (const lll &a,const lll &x)
{
ans=0;
if(a.l==0||x.l==0)
return ans;
for(int i=1;i<=a.l;++i)
{
for(int j=1;j<=x.l;++j)
{
ans.s[i+j-1]+=a.s[i]*x.s[j];
}
}
int jw=0;
ans.l=a.l+x.l-1;
for(int i=1;i<=ans.l;++i)
{
ans.s[i]+=jw;
jw=0;
if(ans.s[i]>=EACH)
{
jw=ans.s[i]/EACH;
ans.s[i]%=EACH;
if(i==ans.l)
{
++ans.l;
}
}
}
return ans;
}
int n,type;
int x,y,z,m;
int a,b[3];
int p,l,r;
int lst[N],sum[N];
int q[N],h=0,t=0;
lll res,data;
void special_read()
{
scanf("%lld%lld%lld%lld%lld%lld",&x,&y,&z,&b[1],&b[2],&m);
for(int i=1,j=1;i<=n&&j<=m;++i)
{
while(p<i) scanf("%lld%lld%lld",&p,&l,&r);
if(i>=3)
b[i%3]=((x*b[(i-1)%3]%MOD+y*b[(i-2)%3]%MOD)%MOD+z)%MOD;
a=b[i%3]%(r-l+1)+l;
sum[i]=sum[i-1]+a;
}
return ;
}
void normal_read()
{
for(int i=1;i<=n;++i)
scanf("%lld",&a),sum[i]=sum[i-1]+a;
return ;
}
signed main()
{
cin>>n>>type;
if(type) special_read();
else normal_read();
q[h]=0;
for(int i=1;i<=n;++i)
{
while(h<t&&sum[q[h+1]]*2-sum[lst[q[h+1]]]<=sum[i]) h++;
lst[i]=q[h];
while(h<t&&sum[q[t]]*2-sum[lst[q[t]]]>=sum[i]*2-sum[lst[i]]) t--;
q[++t]=i;
// for(int j=0;j<i;++j)
// {
// if(sum[i]-sum[j]<lst[j])
// continue;
// if(f[i]>f[j]+(sum[i]-sum[j])*(sum[i]-sum[j]))
// f[i]=f[j]+(sum[i]-sum[j])*(sum[i]-sum[j]),
// lst[i]=sum[i]-sum[j];
// }
}
int tmp=n;
res=0;
while(tmp)
{
data=sum[tmp]-sum[lst[tmp]];
data=data*data;
res=res+data;
tmp=lst[tmp];
}
res.print(),printf("\n");
return 0;
}