Codeforces Global Round 13 C
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