我爱卡常,卡常爱我
题目大意
给你 (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) 的点转移过来的最小值,我们可以易得状态转移方程:
[egin{array}{c}
f_{i,j}=min_{k=0}^{j-1}(f_{j,k}+(sum_i-sum_j)^2)\
s.t.~sum_j-sum_kle sum_i-sum_j
end{array}
]
由于我们是需要枚举 (i,j,k) 的,所以复杂度是 (O(n^3)) 的。
预计得分:(36pts)。
(Part~2:O(n^2))
我们可以发现,对于任意一个位置,最优解是满足最后一组最小的,所以我们可以记录对于每一个位置最后一组的转移点 (lst_i) ,那么转移方程就如下:
[egin{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_ige 2sum_j-sum_{lst_j}
]
又因为 (sum) 数组是满足单调递增的,而且我们要找到最大的 (sum_j) 来使得 (sum_i-sum_j) 也就是最后一段最小,可以考虑单调队列优化,复杂度就为 (O(n)) 了。
预计得分:(88pts)
(Part~4:) 神仙卡常
观察数据范围,我们发现肯定是要写高精的,然后我们发现恶心的出题人,非常善意的卡了你的空间又卡了你的时间。
能开的符合题目条件的 (4 imes 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("
");
return 0;
}