前四道都挺简单的,T5还有一点难度。
题意简述
有 nnn 堆宝石,第 iii 堆有 aia_iai 颗宝石,每隔 kkk 堆选一个,求最多能拿多少宝石。
思路
假设 n=7,k=3,a={4,6,1,2,10,1,3}n=7,k=3,a=\{4,6,1,2,10,1,3\}n=7,k=3,a={4,6,1,2,10,1,3},如图:
可以发现从 111 开始拿和从 k+1k+1k+1 开始拿,一定是前者更优。因为两者后续路线一定会重合,则前者会多拿第一堆,k+2,k+3,…k+2,k+3,\ldotsk+2,k+3,… 同理,因此只需要处理从 i≤ki\le ki≤k 开始拿的情况就行了。
观察从 111 开始拿,拿了 1,4,71,4,71,4,7,可以转换为不拿 2,3,5,62,3,5,62,3,5,6,因此我们有了基本思路:处理前缀和,减去不拿的部分。
仔细观察下标,得到第一组不拿的宝石堆为 fk+1−f1f_{k+1}-f_{1}fk+1−f1,第二组不拿的宝石堆为 f1+2×k+1−f1+k+1f_{1+2\times k+1}-f{1+k+1}f1+2×k+1−f1+k+1。
找规律即得:
对于从 ∀i\forall i∀i 开始拿的情况,不需要拿的宝石数量为
Σx=1i+x×k+x−1≤n(fi+x×k+x−1−fi+(x−1)×k+x−1) \Sigma_{x=1}^{i+x\times k+x-1\le n} (f_{i+x\times k+x-1}-f_{i+(x-1)\times k+x-1}) Σx=1i+x×k+x−1≤n(fi+x×k+x−1−fi+(x−1)×k+x−1)
但是,这一大坨式子是错的。考场60pts调了半个小时没发现
看这种情况:
发现了吗?在最后一堆能拿的宝石如果不是 nnn 的情况下,剩余的宝石堆数不会被剪掉。
所以正确的式子就长这样:
Σx=1i+x×k+x−1≤n(fi+x×k+x−1−fi+(x−1)×k+x−1)−(fn−fi+x×k+x)(i+x×k+x<n) \Sigma_{x=1}^{i+x\times k+x-1\le n} (f_{i+x\times k+x-1}-f_{i+(x-1)\times k+x-1})-(f_n-f_{i+x\times k+x})(i+x\times k+x\lt n) Σx=1i+x×k+x−1≤n(fi+x×k+x−1−fi+(x−1)×k+x−1)−(fn−fi+x×k+x)(i+x×k+x<n)
代码
考场代码:
# include <iostream>
# define LL long long
const int N = 1e6 + 5;
LL n, k, a[N];
LL f[N], g[N], ret ( -1 );
int main () {
std::cin.tie ( 0 ) -> sync_with_stdio ( false );
std::cin >> n >> k;
for ( int i ( 1 ); i <= n; i ++ )
std::cin >> a[i], f[i] = f[i - 1] + a[i];
for ( int i ( 1 ); i <= k + 1; i ++ ) {
int tmp ( 0 );
if ( i >= n - k ) {
g[i] = a[i]; continue;
}
g[i] = f[n] - f[i - 1];
for ( int x ( 1 ); ( i + x *
k + x - 1 ) <= n; x ++ ) {
g[i] -= f[i + x * k + x - 1] -
f[i + ( x - 1 ) * k + x - 1];
tmp = x;
// std::cout << f[i + x * k + x - 1] -
// f[i + ( x - 1 ) * k + x - 1] << ' ';
}
// std::cout << i + tmp * k + tmp - 1 << ' ';
if ( i + tmp * k + tmp < n )
g[i] -= f[n] - f[i + tmp * k + tmp];
// std::cout << f[n] - f[i + tmp * k + tmp - 1] << ' ';
// std::cout << g[i] << '\n';
}
for ( int i ( 1 ); i <= n; i ++ )
ret = std::max ( ret, g[i] );
std::cout << ret << '\n';
return 0;
}