Codeforces Global Round 13 C
大意
略...
思路
首先想到,如果一直贪心选择第一个数,可以得到最优解,证明如下:
考虑第k位的数 (a_k) , 在将其变为1的过程中,所有 (k+2) 到 (k+a_k) 的数都会被免费遍历到一次。
也就是说,无论顺序如何,只要最后整个数组全为1, 那么对于每一个数 (a_i) ,再将其变为1的过程中, (i+2) 到 (i+a_i) 一定都会被免费遍历一次。
容易发现,每一个位置的免费遍历的次数的最大值是确定的,而当我们贪心选择时,总能让所有位置达到最大值。
因为免费遍历的位置只能在选择消减的数之后,所以我们贪心将 (a_1) 变为1,然后,再贪心于 (a_1) 就相当于选择了 (a_2) ,此时 (a_2) 的免费遍历次数显然最大,归纳易得上述发现。
考虑如何求解。
对于第k位的数 (a_k) ,假设已知其免费遍历次数为 (c) ,那么会有如下三种情况:
-
(2 leq a_k - c)
此时,经过免费遍历后 (a_k > 1) 所以还需 (a_k-c-1) 次才能使其变为1。
同时 (k+2) 到 (k+a_k) 的数都会被免费遍历一次。
-
(a_k -c = 1)
此时,(a_k) 正好变为1
-
(a_k - c < 1)
这种情况比较特殊,
(a_k) 的免费遍历次数超过了本身的值。
观察题意会发现,超过的次数会转移到 (k+1) 位。
代码
时间复杂度 (Theta(NlogN))
我是直接复制的树状数组模板,因为是按顺序访问并添加,可以换成手写差分,于是时间复杂度 (Theta(N))
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
#define cint const int&
#define Pi acos(-1)
const int mod = 1e9+7;
const int inf_int = 0x7fffffff;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
int t;
int n;
int a[5050];
int r[5050];
int lowbit(cint x) { return x&-x; }
void add_p(int loc, cint num) {
while(loc <= n) {
r[loc] += num;
loc = loc + lowbit(loc);
}
}
int get_r(int x) {
// 1~x
int ans = 0;
while(x) {
ans += r[x];
x -= lowbit(x);
}
return ans;
}
int main() {
cin >> t;
while(t--) {
cin >> n;
for(int i=1; i<=n; i++) cin >> a[i], r[i] = 0;
ll ans = 0;
for(int i=1; i<=n; i++) {
int s = get_r(i);
// cout << i << ' ' << a[i] << ' ' << s << endl;
if(a[i] - s >= 1) {
ans += a[i]-s-1;
} else {
add_p(i+1, s-a[i]+1);
add_p(i+2, -(s-a[i]+1));
}
add_p(i+2, 1);
add_p(min(n+1, i+a[i]+1), -1);
}
cout << ans << endl;
}
return 0;
}
45min, -4