考场上初一的我用链表瞎模拟得到了 (4 ext{pts}) 的好成绩,时隔一年半,不看题解还是只会 (36 ext{pts}),自闭了……
4pts
输出样例。
12pts
暴力枚举分段点。
36pts
记 (f(i,j)) 表示当前新划分的区间是 ([i,j]),划分完 (1sim j) 的最小代价,那么有转移:
[f(i,j)=min_{sumlimits_{t=k}^{i-1}a_tle sumlimits_{t=i}^ja_t}left{f(k,i-1)+left(sum_{t=i}^{j}a_t
ight)^2
ight}
]
时间复杂度 (mathcal O(n^3)),期望得分 (36 ext{pts})。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int N=410;
int sum[N],a[N],f[N][N];
signed main()
{
freopen("partition.in","r",stdin);
freopen("partition.out","w",stdout);
int n,t;scanf("%lld%lld",&n,&t);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++) f[1][i]=sum[i]*sum[i];
for(int i=2;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
for(int k=1;k<i;k++)
if(sum[j]-sum[i-1]>=sum[i-1]-sum[k-1])
f[i][j]=min(f[i][j],f[k][i-1]);
f[i][j]+=(sum[j]-sum[i-1])*(sum[j]-sum[i-1]);
}
}
int ans=0x3f3f3f3f3f3f3f3fll;
for(int i=1;i<=n;i++)ans=min(ans,f[i][n]);
printf("%lld",ans);
return 0;
}
64pts
一个显然的性质:最后一段的和越小越好。那么可以将状态转为一维,(f(i)) 表示最后一个划分结尾为 (i),(1sim i) 的最少代价,(l_i) 表示满足转移条件的最大的 (j),那么有转移:
[f(i)=min_{0<jle i ext{ 且 }sumlimits_{t=l_j+1}^ja_tle sumlimits_{t=j+1}^i a_t}left{f(j)+left(sum_{t=j+1}^i a_t
ight)^2
ight}
]
时间复杂度 (mathcal O(n^2)),期望得分 (64 ext{pts})。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int N=5e5+10;
int a[N],f[N],l[N],sum[N];
signed main()
{
freopen("partition.in","r",stdin);
freopen("partition.out","w",stdout);
int n,t;scanf("%lld%lld",&n,&t);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<i;j++)
{
if(sum[i]-sum[j]>=sum[j]-sum[l[j]])
{
if(f[j]+(sum[i]-sum[j])*(sum[i]-sum[j])<f[i])
{
f[i]=f[j]+(sum[i]-sum[j])*(sum[i]-sum[j]);
l[i]=j;
}
}
}
}
// for(int i=1;i<=n;i++)printf("%lld ",f[i]);puts("");
printf("%lld",f[n]);
return 0;
}
88pts
将 (sumlimits_{t=l_j+1}^ja_tle sumlimits_{t=j+1}^i a_t) 写成前缀和的形式,得到:
[s_j-s_{l_j}le s_i-s_j
]
移项可得:
[s_ige 2s_j-s_{l_j}
]
现在左边只和 (i) 相关,右边只和 (j) 相关,而我们只需要一个满足这个条件的最大的 (j),所以可以单调队列维护 (2s_j-s_{l_j}),时间复杂度 (mathcal O(n)),期望得分 (88 ext{pts})。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int N=5e5+10;
int a[N],f[N],l[N],sum[N],que[N],h=0,t=0;
signed main()
{
freopen("partition.in","r",stdin);
freopen("partition.out","w",stdout);
int n,t;scanf("%lld%lld",&n,&t);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
for(int i=1;i<=n;i++)
{
while(h<t&&sum[que[h+1]]*2-sum[l[que[h+1]]]<=sum[i])h++;
l[i]=que[h];f[i]=f[l[i]]+(sum[i]-sum[l[i]])*(sum[i]-sum[l[i]]);
while(h<t&&sum[que[t]]*2-sum[l[que[t]]]>=sum[i]*2-sum[l[i]])t--;
que[++t]=i;
}
printf("%lld",f[n]);
return 0;
}
100pts
卡常。
不懂为什么要出这个部分分。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=4e7+10;
typedef long long ll;
ll sum[N];
int a[N],la[N],que[N],h=0,t=0;
int b[N],l[N],r[N],p[N];
void Write(__int128 n)
{
if(n<0) {putchar('-');n=-n;}
if(n>9) Write(n/10);
putchar(n%10+'0');
return;
}
signed main()
{
freopen("partition.in","r",stdin);
freopen("partition.out","w",stdout);
int n,t;scanf("%d%d",&n,&t);
if(t==0)
{
for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
for(int i=1;i<=n;i++)
{
while(h<t&&sum[que[h+1]]*2-sum[la[que[h+1]]]<=sum[i])h++;
la[i]=que[h];
while(h<t&&sum[que[t]]*2-sum[la[que[t]]]>=sum[i]*2-sum[la[i]])t--;
que[++t]=i;
}
__int128 ans=0;
int pos=n;
while(pos)
{
ans+=(__int128)(sum[pos]-sum[la[pos]])*(sum[pos]-sum[la[pos]]);
pos=la[pos];
}
Write(ans);
}
else
{
int x,y,z,m;scanf("%d%d%d%d%d%d",&x,&y,&z,&b[1],&b[2],&m);
for(int i=1;i<=m;i++) scanf("%d%d%d",&p[i],&l[i],&r[i]);
int n=p[m];
for(int i=3;i<=n;i++) b[i]=((ll)x*b[i-1]+(ll)y*b[i-2]+z)%(1ll<<30ll);
for(int i=1;i<=m;i++)
for(int j=p[i-1]+1;j<=p[i];j++)
a[j]=(b[j]%(r[i]-l[i]+1))+l[i],sum[j]=sum[j-1]+a[j];
for(int i=1;i<=n;i++)
{
while(h<t&&sum[que[h+1]]*2-sum[la[que[h+1]]]<=sum[i])h++;
la[i]=que[h];
while(h<t&&sum[que[t]]*2-sum[la[que[t]]]>=sum[i]*2-sum[la[i]])t--;
que[++t]=i;
}
__int128 ans=0;
int pos=n;
while(pos)
{
ans+=(__int128)(sum[pos]-sum[la[pos]])*(sum[pos]-sum[la[pos]]);
pos=la[pos];
}
Write(ans);
}
return 0;
}