Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

another envy pr (no way) #5068

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions content/5_Plat/Segtree_Ext.problems.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@
"kind": "none"
}
},
{
"uniqueId": "usaco-577",
"name": "High Card Low Card",
"url": "http://www.usaco.org/index.php?page=viewproblem2&cpid=577",
"source": "Platinum",
"difficulty": "Easy",
"isStarred": false,
"tags": ["PURQ", "Greedy"],
"solutionMetadata": {
"kind": "internal"
}
},
{
"uniqueId": "usaco-365",
"name": "Optimal Milking",
Expand Down Expand Up @@ -186,19 +198,6 @@
"kind": "internal"
}
},
{
"uniqueId": "usaco-577",
"name": "High Card Low Card",
"url": "http://www.usaco.org/index.php?page=viewproblem2&cpid=577",
"source": "Platinum",
"difficulty": "Hard",
"isStarred": false,
"tags": ["PURQ", "Greedy"],
SansPapyrus683 marked this conversation as resolved.
Show resolved Hide resolved
"solutionMetadata": {
"kind": "USACO",
"usacoId": "577"
}
},
{
"uniqueId": "balkan-18-election",
"name": "2018 - Election",
Expand Down
148 changes: 148 additions & 0 deletions solutions/platinum/usaco-577.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
---
id: usaco-577
source: USACO Platinum 2015 December
title: High Card Low Card
author: Chongtian Ma
---

[Official Analysis (C++)](https://usaco.org/current/data/sol_cardgame_platinum_dec15.html)

## Explanation

First, let's determine the cards that bessie will play for each mode.
Intuitively, if she plays $x$ turns of the "higher mode", then she must play the
$x$ highest cards in some order. Her score for "higher mode" can't worsen if she
switches out any card for a lower-numbered card. Even if she does, it might
worsen her score for the "lower mode". As for her strategy, she always wants to
play the smallest card available **among all $x$ cards** that is greater than
Elsie's card. Therefore, the following algorithm is always optimal for "highest
mode":

```
Let E = An array of Elsie's first x cards
let B = An array of Bessie's highest x cards
score = 0
for d from 1 to 2n:
for elsie_card in E:
if bessie_card with number (elsie_card + d) exists in B:
remove elsie_card from E
remove bessie_card from B
add 1 to score
```

Directly implementing the above algorithm yields quadratic time for each $x$,
which is too slow. We can use a segment tree to speed things up.

Essentially, the segment tree calculates the score for each $d$ within a
interval of length $2^k$, where $k$ is the depth of the vertex from its leaves.
For each vertex of the segment tree, we have to track the following:

- The maximum achievable score after pairing all Bessie Cards with Elsie Cards
in the interval.
- The number of Elsie cards that have not been paired with a Bessie Card.
- The number of Bessie cards that have not been paired with an Elsie Card.

To merge two intervals into a bigger interval, we can pair all unpaired Elsie
cards in the left interval with unpaired Bessie cards in the right interval.
Therefore, the minimum of these two values will contribute to the score for the
bigger interval.

The algorithm for "lower mode" can be determined similarly.

## Implementation

**Time Complexity:** $\mathcal{O}(N \log N)$

<LanguageSection>

<CPPSection>

```cpp
#include <bits/stdc++.h>
using namespace std;

struct segtree {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(optional)

don't see a well-defined c++ standard for class/struct names, but most of the solution class/struct names here use pascalcase.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

classes/structs should always use pascalcase here

bool higher_mode;
int n;

struct item {
int ans, bessie, elsie;
};

item merge(item a, item b) {
int take = higher_mode ? min(a.elsie, b.bessie) : min(a.bessie, b.elsie);
return {a.ans + b.ans + take, a.bessie + b.bessie - take,
a.elsie + b.elsie - take};
}

item NEUTRAL = {0, 0, 0};

vector<item> seg;

segtree(int n, bool higher_mode) {
this->n = n;
this->higher_mode = higher_mode;
seg.resize(2 * n, NEUTRAL);
}

void update(int idx, item x) {
idx += n;
seg[idx] = x;
while (idx /= 2) { seg[idx] = merge(seg[2 * idx], seg[2 * idx + 1]); }
}

int query() {
item left_item = NEUTRAL, right_item = NEUTRAL;
for (int l = n, r = 2 * n; l < r; l /= 2, r /= 2) {
if (l % 2 == 1) { left_item = merge(left_item, seg[l++]); }
if (r % 2 == 1) { right_item = merge(seg[--r], right_item); }
}
return merge(left_item, right_item).ans;
}
};

int main() {
ifstream cin("cardgame.in");
ofstream cout("cardgame.out");

int n;
cin >> n;
vector<bool> contain(2 * n);
vector<int> elsie;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
contain[--x] = true;
elsie.push_back(x);
}
vector<int> bessie;
for (int i = 0; i < 2 * n; i++) {
if (!contain[i]) { bessie.push_back(i); }
}

segtree higher_st(2 * n, true), lower_st(2 * n, false);
const segtree::item BESSIE_CARD = {0, 1, 0}, ELSIE_CARD = {0, 0, 1},
RESET = {0, 0, 0};

for (int i = 0; i < n; i++) {
higher_st.update(bessie[i], BESSIE_CARD);
higher_st.update(elsie[i], ELSIE_CARD);
}

int ans = higher_st.query();
// sort to allocate smallest cards first for "lower mode"
sort(bessie.rbegin(), bessie.rend());
TheGamingMousse marked this conversation as resolved.
Show resolved Hide resolved
for (int i = n - 1; i >= 0; i--) {
higher_st.update(bessie[i], RESET);
higher_st.update(elsie[i], RESET);
lower_st.update(bessie[i], BESSIE_CARD);
lower_st.update(elsie[i], ELSIE_CARD);
ans = max(ans, higher_st.query() + lower_st.query());
}
cout << ans << "\n";
}
```

</CPPSection>

</LanguageSection>
Loading