题解:P10299 [CCC 2024 S5] Chocolate Bar Partition
更好的阅读体验
先将所有格子的权值都减去这些格子的平均数。为了方便,我们先将所有格子的权值乘以
我们可以先考虑一个朴素的 dp:设
然而我们发现很多状态是无用的。具体地,我们只关心一个连通块的最后一个格子在哪里,即要从哪里划分连通块。所以由上面的状态设计,一个有用的状态,一定是上面的格子或下面的格子所在连通块的和为
我们设
- 将上下的格子都划分到前一列不为
0 的格子所在连通块。则连通块数量不会增加,从\max(f_{j, i-1}, f_{k, i-1}) 转移。 - 将第
k 行末尾的若干个格子形成一个和为0 的连通块,从f_{k, l} + 1 转移,其中l 是一个合法转移点满足\sum\limits_{x=l+1}^{i}a_{k, x} = 0 。 - 将第
k 行末尾的若干个格子和前面不为0 的格子形成一个和为0 的连通块,从f_{k, l} + 1 转移,其中l 是一个合法转移点满足\sum\limits_{x=l+1}^{x}a_{k, x} + \sum\limits_{x=1}^{l}a_{k, x} + \sum\limits_{x=1}^{l}a_{j, x} = 0 。 - 该行上下格子所在的格子连通块和都为
0 ,这种条件需满足\sum \limits_{x=1}^{i}a_{j, x}+\sum \limits_{x=1}^{i}a_{k, x} = 0 ,用\max(f_{j, i}, f_{k, i}) + 1 转移,表示可以新开一个连通块。
第一和第四个转移直接做就好了。第二、三开一个桶存满足条件的最大
那么这道题就做完了,复杂度
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define N 800006
using namespace std;
inline void chkmax(int &x,int y){x=x<y?y:x;}
int n,bn,sum,b[N],a[2][N],s[2][N],s_[2][N];
int f[2][N],g[4][N];
main()
{
scanf("%lld",&n);
for(int i=0;i<2;i++)
for(int j=1;j<=n;j++)scanf("%lld",&a[i][j]),sum+=a[i][j];
for(int i=0;i<2;i++)
for(int j=1;j<=n;j++)a[i][j]=a[i][j]*2*n-sum+a[i][j-1];
for(int i=0;i<2;i++)
for(int j=0;j<=n;j++)b[++bn]=a[i][j],b[++bn]=-a[i][j];
sort(b+1,b+1+bn),bn=unique(b+1,b+1+bn)-b-1;
for(int i=0;i<2;i++)
for(int j=0;j<=n;j++)s[i][j]=lower_bound(b+1,b+1+bn,a[i][j])-b;
for(int i=0;i<2;i++)
for(int j=0;j<=n;j++)s_[i][j]=lower_bound(b+1,b+1+bn,-a[i][j])-b;
memset(g,-0x3f,sizeof(g));
for(int i=1;i<=n;i++)
{
for(int j=0;j<2;j++)
chkmax(g[j][s[j][i-1]],f[j^1][i-1]);
for(int j=0;j<2;j++)
chkmax(g[j|2][s[j^1][i-1]],f[j][i-1]);
for(int j=0;j<2;j++)
chkmax(f[j][i],max(f[0][i-1],f[1][i-1]));
for(int j=0;j<2;j++)
chkmax(f[j][i],max(g[j^1][s[j^1][i]],g[(j^1)|2][s_[j^1][i]])+1);
if(a[0][i]+a[1][i]==0)f[0][i]=f[1][i]=max(f[0][i],f[1][i])+1;
}
printf("%lld\n",f[0][n]);
return 0;
}