https://atcoder.jp/contests/abc440/tasks/abc440_c
思路:
1. 核心数学转化:从"下标"到"余数"
题目的涂色条件是:当 (i+x) mod 2W < W时,涂黑第 i 个方格。
我们可以将这个不等式理解为:对于一个固定的 x,凡是 i mod 2W 落在某个特定范围内的方格都会被涂黑。在一个周期 2W 内,满足条件的余数恰好有 W 个。由于 x 是可以自由选择的正整数,这 W 个余数在模 2W 的意义下是连续 的。 我们的目标是找到模 2W 的 2W 个余数中,连续的 W 个余数,使得这些余数对应的方格成本之和最小。
第一步:压缩(Mapping)
vector<ll> a(n+1), vec(4*w);
rep(i,1,n) {
cin>>a[i];
vec[i%(2*w)]+=a[i]; // 将方格成本按余数分类累加
}
- 方格数量 N 可能很大,但余数只有 2W 个。
vec[r]存储了所有满足的方格
之和。这一步将
的空间复杂度压缩到了
。
第二步:断环成链
rep(i,2*w, 4*w-1) vec[i] = vec[i-2*w];
- 因为余数是环状的(例如
的下一个余数是 0),为了方便处理"跨越边界"的连续区间,代码将
vec数组复制了一份接在后面。这样,任何在环上连续的 W 个元素,在长度为 4W 的线性数组中都会表现为一段普通的连续区间。
第三步:滑动窗口
ll ans=INF, tt=0;
ll l=0, r=-1;
while (r < 4*w) {
while (r-l+1 < w) tt += vec[++r]; // 窗口不足 W,向右扩展
ans = min(ans, tt); // 更新当前窗口的最小和
if (r == 4*w-1) break;
tt -= vec[l++]; // 窗口左端向右移,准备下一次滑动
}
- 这是一个标准的双指针滑动窗口。窗口长度始终保持为 W。
tt维护当前窗口内余数成本的总和。遍历过程中,ans会记录下所有可能的 W 长度区间中的最小值。
完整ac代码:
void solve() {
ll n,w;cin>>n>>w;
vector<ll>a(n+1),vec(4*w);
rep(i,1,n) {
cin>>a[i];
vec[i%(2*w)]+=a[i];
}
rep(i,2*w,4*w-1)vec[i]=vec[i-2*w];
ll ans=INF,tt=0;
ll l=0,r=-1;
while (r<4*w) {
while (r-l+1<w)tt+=vec[++r];
ans=min(ans,tt);
if (r==4*w-1)break;
tt-=vec[l++];
}
cout<<ans<<endl;
}