Solution of Codeforces Round #824 — editorial
Codeforces Round #824 — editorial
Let's consider that , , and are sorted working segments. Can we explicitly say something about one of them?
must be equal to .
Let's consider that , , and are sorted working segments.
If is not equal to then we can decrease by and increase by . So we'll increase the answer.
We've got that and we have to work just with and .
Now, our problem can be rewritten as:
, maximize .
And as we know that , just:
maximize .
If we increase both values under the minimum scope by one, solutions don't change:
maximize .
If we choose , then .
If the answer is greater, then and , and it means that but .
The only thing is left to do is to calculate final answer. And it is or just .
It was a mathematician way of solving. As it's pretty obvious that is approximately , you could check and choose the best among them.
- #include <bits/stdc++.h>
- using namespace std;
- void solve() {
- int n;
- cin >> n;
- int l_2 = (n - 3) / 3;
- int ans = l_2 - 1;
- cout << ans << '\n';
- }
- int main() {
- ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
- int t = 1;
- cin >> t;
- while (t--)
- solve();
- return 0;
- }
Is there a way to cut pieces to save the minimum value and satisfy required conditions?
What is the minimum possible number of operations to perform it?
Is there any better solution?
Let's start with a simple solution.
Let's choose the minimum piece from and assume that it will remain the minimum until the end.
As the array is sorted, let's define the minimum piece as .
It means that in the end, all pieces must be smaller or equal to .
The lower bound of the answer for this solution is .
Let's show that this is achievable.
For each piece, while its size greater than , let's cut off a piece of size .
The only problem is that we could get a piece smaller than in the end.
But it means that before the last cut we had a piece in the range . All pieces in this range can be easily cut into pieces of the right size in one move.
The only left question is why the minimum piece in the end should have size . Actually, it shouldn't, but it gives the best answer anyway.
As was described above, the lower bound of the solution with the minimal piece of size in the end is .
Having a minimal piece with a smaller size, we can't get anything better, because the lower bound will be equal or greater for all .
- #include <bits/stdc++.h>
- using namespace std;
- int n;
- vector<int> a;
- void solve() {
- cin >> n;
- a.resize(n);
- int ans = 0;
- for (auto &i : a) {
- cin >> i;
- ans += (i - 1) / (2 * a[0] - 1);
- }
- cout << ans << '\n';
- }
- int main() {
- ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
- int t = 1;
- cin >> t;
- while (t--)
- solve();
- return 0;
- }
What is the first letter in the answer?
if doesn't start with and otherwise.
Ask the same question as Hint1 for each position.
When we can't choose the minimum unused letter?
If we form a circle of size less then .
Maintain any structure to check it.
First of all, the encryption process is reversible. If we obtained from using the circle , we can obtain from using the same cycle , but reversed.
So, let's think in terms of encryption of .
Lexicographical order itself is a greedy thing. So, we can create a greedy algorithm.
Let's go from left to right and generate the result letter by letter. We have to choose the best possible option at each step. Let's describe the options we have.
- If the current letter was used earlier, we already know the replacement we need to choose.
- Otherwise, we would like to choose the minimum possible option. We need to maintain some structure to know what is acceptable.
Let's keep the circle that is already generated(it's a graph). For each letter we have one incoming edge and one outgoing edge in the end. Let's keep them for every letter: arrays , .
When we want to generate an outgoing edge at some step(let's define the letter on this step as ), we have to choose the minimum letter that doesn't have an incoming edge yet. With one exception: if creating the edge using this rule creates a circle of size less than . It would mean that we wouldn't have a full circle in the end. It's easy to see that there is no more than one such letter, as this letter is just the end of a chain starting in .
To check that a small circle wasn't generated, we can go along an outgoing edge times, starting at . If we end up in or there was no edge at some step then everything is ok, we can create this edge.
Complexity is , that is, .
- #include <bits/stdc++.h>
- using namespace std;
- int n;
- vector<int> a;
- void solve() {
- cin >> n;
- a.resize(n);
- int ans = 0;
- for (auto &i : a) {
- cin >> i;
- ans += (i - 1) / (2 * a[0] - 1);
- }
- cout << ans << '\n';
- }
- int main() {
- ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
- int t = 1;
- cin >> t;
- while (t--)
- solve();
- return 0;
- }
How many sets can fit in cards?
At most two.
If there are two sets among cards, there will be a central card.
Consider each card as a central card.
For every two cards, there is always a single card that forms a set with them.
[1] That means that two sets can share at most one card.
Let's prove that there are no more than sets in a meta-set.
Let's define cards as . Let's guess that is a set.
All other sets can have at most one card among (according to [1]), so they must include and . So we have at most one other set, otherwise they would have two same cards, which is prohibited according to [1].
So, every meta-set looks like sets with one common card. Let's call this card a central card.
Now there is just a simple combinatorics. For each card, we want to know the number of sets that include it. If this number is , then we should add to the answer — it is the number of meta-sets with this card as a central card.
To get the number of sets for each card, we can iterate over all pairs of cards , generate the complement to the set, and add to that card in a map/hashmap.
Complexity is or .
- #include <bits/stdc++.h>
- using namespace std;
- #define F first
- #define S second
- typedef long long ll;
- typedef long double ld;
- typedef pair<ll, ll> pll;
- typedef pair<int, int> pii;
- const long long kk = 1000;
- const long long ml = kk * kk;
- const long long mod = ml * kk + 7;
- const long long inf = ml * ml * ml + 7;
- #ifdef DEBUG
- mt19937 rng(1033);
- #else
- mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
- #endif
- int rnd(int mod) { return uniform_int_distribution<int>(0, mod - 1)(rng); }
- bool viv = false;
- int n, k;
- vector<vector<int>> v;
- vector<int> get_comp(vector<int> a, vector<int> b) {
- vector<int> res(k);
- for (int i = 0; i < k; i++)
- res[i] = (6 - (a[i] + b[i])) % 3;
- return res;
- }
- void solve() {
- cin >> n >> k;
- v.resize(n);
- for (auto &vec : v) {
- vec.resize(k);
- for (auto &i : vec)
- cin >> i;
- }
- map<vector<int>, int> cnt;
- for (int i = 0; i < n; i++) {
- for (int j = i + 1; j < n; j++) {
- auto comp = get_comp(v[i], v[j]);
- cnt[comp]++;
- }
- }
- ll ans = 0;
- for (auto vec : v) {
- ans += (ll)cnt[vec] * (cnt[vec] - 1) / 2;
- }
- cout << ans << '\n';
- }
- int main() {
- // viv = true;
- ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
- cout << fixed << setprecision(20);
- int t = 1;
- // cin >> t;
- while (t--)
- solve();
- #ifdef DEBUG
- cerr << "Runtime is: " << clock() * 1.0 / CLOCKS_PER_SEC << endl;
- #endif
- }
How many possible options are there for the distance between and .
We can limit it with options.
Consider options , . Solve each one in linear(almost) time.
Consider the biggest distance among and .
Can we match it with something?
Remove them one by one while they exceed the distance between and . Then the problem is trivial.
Let's assume that considered point was to the left of considered point .
Let's assume that we know the distance between considered points and . Let's show how to solve this problem in linear time(almost linear).
As long as there is a value greater than , let's get the largest among them(let's call it ). Let's assume that this value is from .
It's easy to see that this point is to the right of the considered point (because the largest distances is to the point ). It means that we can match distance from to the distance from .
When there is no value greater than , all other houses are located between considered points. We can match them by sorting.
That is the solution.
Let's limit possible options of with options.
If we know that some house has distances and to considered options, then there are options of : and .
Let's consider options , .
Complexity is .
- #include <bits/stdc++.h>
- using namespace std;
- #define F first
- #define S second
- typedef long long ll;
- typedef long double ld;
- typedef pair<ll, ll> pll;
- typedef pair<int, int> pii;
- const long long kk = 1000;
- const long long ml = kk * kk;
- const long long mod = ml * kk + 7;
- const long long inf = ml * ml * ml + 7;
- #ifdef DEBUG
- mt19937 rng(1033);
- #else
- mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
- #endif
- int rnd(int mod) { return uniform_int_distribution<int>(0, mod - 1)(rng); }
- bool viv = false;
- int n;
- vector<int> d1, d2;
- bool work(int points_diff) {
- int p1 = 0;
- int p2 = points_diff;
- multiset<int, greater<int>> s1, s2;
- for (auto i : d1)
- s1.insert(i);
- for (auto i : d2)
- s2.insert(i);
- auto farthest = [] (multiset<int, greater<int>> &s) {
- return s.empty() ? -1 : *s.begin();
- };
- auto farthest_both = [&]() {
- return max(farthest(s1), farthest(s2));
- };
- vector<int> ans;
- while (farthest_both() > points_diff) {
- bool choose_s1 = farthest(s1) > farthest(s2);
- auto &s_far = choose_s1 ? s1 : s2;
- auto &s_near = choose_s1 ? s2 : s1;
- int value = *s_far.begin();
- int complem = value - points_diff;
- if (!s_near.count(complem))
- return false;
- s_far.erase(s_far.find(value));
- s_near.erase(s_near.find(complem));
- if (choose_s1)
- ans.push_back(p1 + value);
- else
- ans.push_back(p2 - value);
- }
- vector<int> left1, left2;
- for (auto i : s1)
- left1.push_back(i);
- for (auto i : s2)
- left2.push_back(i);
- sort(left1.begin(), left1.end());
- sort(left2.rbegin(), left2.rend());
- for (int i = 0; i < left1.size(); i++)
- if (left1[i] + left2[i] != points_diff)
- return false;
- for (auto i : left1)
- ans.push_back(i);
- sort(ans.begin(), ans.end());
- int sh = max(-ans[0], 0);
- p1 += sh, p2 += sh;
- for (auto &i : ans)
- i += sh;
- cout << "YES\n";
- for (auto i : ans)
- cout << i << ' ';
- cout << '\n';
- cout << p1 << ' ' << p2 << '\n';
- return true;
- }
- void solve() {
- cin >> n;
- d1.resize(n);
- d2.resize(n);
- for (auto &d : d1)
- cin >> d;
- for (auto &d : d2)
- cin >> d;
- int dist1 = d1[0];
- for (auto dist2 : d2) {
- if (work(dist1 + dist2))
- return;
- if (work(abs(dist1 - dist2)))
- return;
- }
- cout << "NO\n";
- }
- int main() {
- // viv = true;
- ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
- cout << fixed << setprecision(20);
- int t = 1;
- cin >> t;
- while (t--)
- solve();
- #ifdef DEBUG
- cerr << "Runtime is: " << clock() * 1.0 / CLOCKS_PER_SEC << endl;
- #endif
- }
Will be added soon
- #include <bits/stdc++.h>
- using namespace std;
- #define F first
- #define S second
- typedef long long ll;
- typedef long double ld;
- typedef pair<ll, ll> pll;
- typedef pair<int, int> pii;
- const long long kk = 1000;
- const long long ml = kk * kk;
- const long long mod = ml * kk + 7;
- const long long inf = ml * ml * ml + 7;
- const ld eps = 1e-9;
- #ifdef DEBUG
- mt19937 rng(1033);
- #else
- mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
- #endif
- int rnd(int mod) { return uniform_int_distribution<int>(0, mod - 1)(rng); }
- int n, a, b;
- vector<int> p, q;
- struct Seg {
- ll init_x, init_y;
- int num;
- ld x, y;
- Seg() {}
- Seg(int p, int q, int num): init_x(q), init_y(-p), num(num) {
- x = init_x;
- y = init_y;
- }
- friend bool operator<(const Seg &s1, const Seg &s2) {
- if (s1.init_x * s2.init_y == s1.init_y * s2.init_x)
- return s1.num < s2.num;
- return s1.init_x * s2.init_y < s1.init_y * s2.init_x;
- }
- Seg operator*=(ld ratio) { x *= ratio; y *= ratio; return *this; }
- void show() {
- cout << "Seg(" << x << ' ' << y << ")";
- }
- };
- struct Point {
- ld x, y;
- Point() {}
- Point(int a, int b): x(b), y(a) {}
- Point operator+=(const Point &v) { x += v.x; y += v.y; return *this; }
- Point operator-=(const Point &v) { x -= v.x; y -= v.y; return *this; }
- Point operator+=(const Seg &v) { x += v.x; y += v.y; return *this; }
- Point operator-=(const Seg &v) { x -= v.x; y -= v.y; return *this; }
- void show() {
- cout << "Point(" << x << ' ' << y << ")";
- }
- };
- struct Stock {
- Point best_a;
- Point best_b;
- multiset<Seg> segs;
- Stock(int a, int b) {
- best_a = Point(a, 0);
- best_b = Point(0, b);
- Seg seg_1 = Seg(a, 0, -2);
- Seg seg_2 = Seg(0, b, -1);
- if (a)
- segs.insert(seg_1);
- if (b)
- segs.insert(seg_2);
- }
- void add_seg(int p, int q, int num) {
- Seg new_seg = Seg(2 * p, 2 * q, num);
- segs.insert(new_seg);
- Point shift(p, -q);
- best_a += shift;
- best_b -= shift;
- cut_left();
- cut_down();
- }
- void cut_left() {
- while (best_a.x < 0) {
- Seg l_seg = *segs.begin();
- Point new_best_a = best_a;
- new_best_a += l_seg;
- if (new_best_a.x > eps) {
- ld ratio = new_best_a.x / l_seg.x;
- Seg l_seg_left = l_seg;
- l_seg_left *= ratio;
- segs.erase(segs.find(l_seg));
- segs.insert(l_seg_left);
- new_best_a -= l_seg_left;
- new_best_a.x = max(new_best_a.x, (ld)0);
- } else {
- segs.erase(segs.begin());
- }
- best_a = new_best_a;
- }
- }
- void cut_down() {
- while (best_b.y < 0) {
- Seg d_seg = *segs.rbegin();
- Point new_best_b = best_b;
- new_best_b -= d_seg;
- if (new_best_b.y > eps) {
- ld ratio = new_best_b.y / -d_seg.y;
- Seg d_seg_left = d_seg;
- d_seg_left *= ratio;
- segs.erase(segs.find(d_seg));
- segs.insert(d_seg_left);
- new_best_b += d_seg_left;
- new_best_b.y = max(new_best_b.y, (ld)0);
- } else {
- segs.erase(segs.find(d_seg));
- }
- best_b = new_best_b;
- }
- }
- void print_best() {
- cout << best_a.y << '\n';
- }
- void show() {
- cout << "----\tStock\t----\n";
- cout << "best_a = ";
- best_a.show();
- cout << endl;
- Point cur = best_a;
- for (auto seg : segs) {
- cur += seg;
- cout << "\t";
- cur.show();
- cout << endl;
- }
- cout << "best_b = ";
- best_b.show();
- cout << endl;
- cout << "----\tEnd\t----\n\n";
- }
- };
- void solve() {
- cin >> n >> a >> b;
- p.resize(n);
- q.resize(n);
- for (int i = 0; i < n; i++)
- cin >> p[i];
- for (int i = 0; i < n; i++)
- cin >> q[i];
- Stock st(a, b);
- for (int i = 0; i < n; i++) {
- st.add_seg(p[i], q[i], i);
- st.print_best();
- }
- }
- int main() {
- ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
- cout << fixed << setprecision(20);
- int t = 1;
- cin >> t;
- while (t--)
- solve();
- #ifdef DEBUG
- cerr << "Runtime is: " << clock() * 1.0 / CLOCKS_PER_SEC << endl;
- #endif
- }
Comments
Post a Comment