Розбір
Problem Author: | Illia Fursov |
Prepared by: | Illia Fursov, Illia Shevchenko |
Editorial by: | Illia Fursov |
Group 1: for all .
If someone moves forward — then someone needs to move back.
Taking this fact into account, we cannot move any of the customers at all, and the only valid order of people in a queue is the initial one, and the answer is just .
To show that this sum can be calculated in time complexity, let's open the parentheses and see that: is used in every sum, is used in every sum, except for the first one, ..., is used in sums. That means that the answer can be written down as: .
Time and memory complexity:
Group 2: .
In this group, is small enough to allow checking all the possible orders / permutations of people, checking all the conditions for them and taking the minimum possible sum out of them all.
Time complexity:
Memory complexity:
Group 3: .
In this group, checking all the permutations will not already pass, but there is still another solution — dynamic programming over bitmasks.
We can consider as a minimum result for the of places, taken by the first (the number of -bits in ) customers, and updates in this case are "setting the 'th customer on the place which is not taken yet if the -condition is satisfied". The answer for the problem is (i.e. the minimum possible result when all the places are taken).
Time complexity:
Memory complexity:
Group 4: for all .
This group is, technically, the opposite for the first one. In that group we could not move any of the customers, and in this group we can move any customer at any position. It holds because , which means that every customer is fine even with moving to the very back of the queue.
So, now we have a total freedom of how to reorder the customers. To find out the most optimal order, let's look at the formula we got solving the first group: . Here, the answer can be calculated the same way, but the order of 's near coefficients can be any other.
The most optimal order of 's here is actually their increasing order.
The answer can be calculated by sorting the array and using the formula, mentioned in the beginning.
Time complexity: (because of sorting)
Memory complexity:
Groups 5, 6 and 7 require different implementations to be solved, but use the same idea.
And the idea is: to get the best result, we need to reorder the customers in decreasing order of their 's, taking for each the largest position that is still possible to take (the largest such position that it is not yet taken and )
To prove this idea, let's prove three facts:
All the -conditions will be satisfied;
The generated order is complete and valid (all the customers will take each position from to );
Out of all the possible orders, there are no better orders than the generated one.
Group 5: .
In this group, placing of each person can be done by iterating through all the positions from to and taking the maximum free one. In this case, placing each person takes time and there are people in total. Thus:
Time complexity:
Memory complexity:
Group 6: .
In this group, all the people can be grouped by their 's and reordered using the same algorithm as in the previous group (if there are some people with the same 's, then is does not matter who will be placed earlier).
Time complexity:
Memory complexity:
Group 7: no additional constraints.
In this group, we can use a set to store free positions, and take the maximum available one, which is not greater than using set.lower_bound() / set.upper_bound() function (positions can be stored in form of their negative values if it's more convenient). Then placing each of customers will take time, which results in acceptable time complexity.
Full implementation (C++):
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 12; int n; int a[N], t[N]; int p[N]; int q[N]; bool cmp(int i, int j) { return t[i] > t[j]; } signed main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n; for (int i = 0; i < n; i++) cin >> t[i]; for (int i = 0; i < n; i++) cin >> a[i]; iota(p, p + n, 0); sort(p, p + n, cmp); set <int> unused; for (int i = 0; i < n; i++) unused.insert(-i); for (int pi = 0; pi < n; pi++) { int i = p[pi]; int c = min(n - 1, i + a[i]); auto now = unused.lower_bound(-c); assert(now != unused.end()); // making sure that there is at least one available position int pos = -(*now); unused.erase(now); q[pos] = i; } ll ans = 0, sum = 0; for (int i = 0; i < n; i++) { assert(i <= q[i] + a[q[i]]); // making sure that all a[i] conditions are satisfied sum += t[q[i]]; ans += sum; } cout << ans << endl; }
Time complexity:
Memory complexity: