## 1. Abridged problem statement

During an ICPC-style contest, standings are frozen after minute 240. You are given the frozen standings table, the total number of balloons given during the last hour (minutes 241–300), and your team name.

A balloon corresponds to a problem accepted during the last hour. If a problem is accepted at minute 241 (earliest), the penalty is:

```text
241 + 20 * number_of_previous_wrong_submissions_on_that_problem
```

Frozen standings show, for each team and problem: solved (`+x`), unsolved with some rejects (`-x`), or never submitted (`.`).

Given the frozen standings and total last-hour balloons, determine the **best** (minimum) and **worst** (maximum) possible final rank of your team.

Ranks: more solved problems is better; equal solved uses smaller penalty; equal solved and penalty means shared rank. Rank = number of strictly better teams + 1.

Multiple test cases until EOF.

---

## 2. Detailed editorial

### Observations

- Any accepted submission during the last hour can be assumed to happen at minute 241 (minimizing penalty).
- For each unsolved problem with `x` previous rejects, solving it in the last hour adds minimum `241 + 20 * x` to penalty.
- Extra wrong submissions during the last hour can only increase penalty, so we never need them in our greedy best-case.

### Parsing

Team names may contain spaces. So the right-hand part of each standings row (problem results, penalty, solved count) is parsed from right to left. The rank appears at the very left, and everything between rank and solved count is the name.

### Best possible rank

To minimize our rank (get the best place):

1. Give our team as many last-hour accepted problems as possible.
2. Each time, solve the unsolved problem with the fewest previous rejects (minimizes added penalty).
3. If balloons remain after our team finishes its unsolved problems, they can usually be absorbed by other teams harmlessly. The edge case: if our team is last AND every other team above has already solved all problems, leftover balloons are forced on us as extra rejects (+20 penalty each).
4. Once our team's state is updated, scan upward through teams originally above us and count how many no longer strictly beat us. Because standings are sorted, stop at the first team that still beats us.

### Worst possible rank

To maximize our rank (get the worst place):

1. Keep our team unchanged.
2. Move our team to the beginning of its tied-rank block (teams with same solved + penalty). This is safe because they're equal, but lets the tied-but-higher-positioned peers count as potential overtakers below us.
3. Process teams below us in groups with the same frozen solved count. Let `det = my_solved - group_solved`.
   - Each team in the group needs `det` balloons to tie our solved count, or `det + 1` to exceed it.
   - First simulate cheapest `det` solves for every team in the group (update their state).
   - Greedily count teams that overtake with `det` balloons (better penalty after tying solved count).
   - Then count teams that overtake with `det + 1` balloons (one extra problem makes them strictly better).
4. Stop if not enough balloons remain to let even one more team reach our solved count.

### Complexity

Let T <= 100 teams, P <= 26 problems. Each accept scans at most P problems. Overall O(T * P^2), trivially within limits.

---

## 3. C++ Solution

```cpp
#include <bits/stdc++.h>

using namespace std;

template<typename T1, typename T2>
ostream& operator<<(ostream& out, const pair<T1, T2>& x) {
    return out << x.first << ' ' << x.second;
}

template<typename T1, typename T2>
istream& operator>>(istream& in, pair<T1, T2>& x) {
    return in >> x.first >> x.second;
}

template<typename T>
istream& operator>>(istream& in, vector<T>& a) {
    for(auto& x: a) {
        in >> x;
    }
    return in;
};

template<typename T>
ostream& operator<<(ostream& out, const vector<T>& a) {
    for(auto x: a) {
        out << x << ' ';
    }
    return out;
};

struct TeamInfo {
    string name;
    int solved;
    int64_t penalty;
    int rank;
    vector<int> rejects_unsolved;
};

int n_problems;
vector<TeamInfo> teams;
int balloons;
string my_team;

bool read_nonempty_line(string& line) {
    while(getline(cin, line)) {
        for(char c: line) {
            if(!isspace((unsigned char)c)) {
                return true;
            }
        }
    }
    return false;
}

bool is_team_row(const string& line) {
    for(char c: line) {
        if(c == '.' || c == '+' || c == '-') {
            return true;
        }
    }
    return false;
}

TeamInfo parse_team_row(const string& line) {
    TeamInfo t;
    int last = (int)line.size() - 1;
    while(last >= 0 && isspace((unsigned char)line[last])) {
        last--;
    }
    auto skip_spaces = [&]() {
        while(last >= 0 && line[last] == ' ') {
            last--;
        }
    };
    auto read_digits_back = [&]() {
        int64_t val = 0, base = 1;
        while(last >= 0 && isdigit((unsigned char)line[last])) {
            val += base * (line[last] - '0');
            base *= 10;
            last--;
        }
        return val;
    };
    for(int p = 0; p < n_problems; p++) {
        skip_spaces();
        int64_t num = read_digits_back();
        skip_spaces();
        char sign = line[last--];
        if(sign != '+') {
            t.rejects_unsolved.push_back((int)num);
        }
    }
    skip_spaces();
    t.penalty = read_digits_back();
    skip_spaces();
    t.solved = (int)read_digits_back();

    int s = 0;
    while(s < (int)line.size() && line[s] == ' ') {
        s++;
    }
    int rank = 0;
    while(s < (int)line.size() && isdigit((unsigned char)line[s])) {
        rank = rank * 10 + (line[s] - '0');
        s++;
    }
    while(s < (int)line.size() && line[s] == ' ') {
        s++;
    }
    while(last >= s && line[last] == ' ') {
        last--;
    }
    t.rank = rank;
    t.name = line.substr(s, last - s + 1);
    return t;
}

bool read() {
    string line;
    if(!read_nonempty_line(line)) {
        return false;
    }
    {
        istringstream iss(line);
        string tok;
        iss >> tok >> tok >> tok >> tok;
        n_problems = 0;
        while(iss >> tok) {
            n_problems++;
        }
    }
    teams.clear();
    while(true) {
        if(!read_nonempty_line(line)) {
            return false;
        }
        if(!is_team_row(line)) {
            balloons = stoi(line);
            break;
        }
        teams.push_back(parse_team_row(line));
    }
    if(!read_nonempty_line(line)) {
        return false;
    }
    int s = 0;
    while(s < (int)line.size() && isspace((unsigned char)line[s])) {
        s++;
    }
    int e = (int)line.size();
    while(e > s && isspace((unsigned char)line[e - 1])) {
        e--;
    }
    my_team = line.substr(s, e - s);
    return true;
}

bool better_than(const TeamInfo& a, const TeamInfo& b) {
    if(a.solved != b.solved) {
        return a.solved > b.solved;
    }
    return a.penalty < b.penalty;
}

void accept_one(TeamInfo& t) {
    auto it = min_element(t.rejects_unsolved.begin(), t.rejects_unsolved.end());
    t.solved++;
    t.penalty += 241 + 20 * (int64_t)(*it);
    t.rejects_unsolved.erase(it);
}

int high_pos(vector<TeamInfo> ts, int my_idx, int balloons_left) {
    TeamInfo& me = ts[my_idx];
    while(balloons_left > 0 && !me.rejects_unsolved.empty()) {
        accept_one(me);
        balloons_left--;
    }
    int n = (int)ts.size();
    bool absorbed_elsewhere;
    if(my_idx != n - 1) {
        absorbed_elsewhere = true;
    } else if(my_idx == 0) {
        absorbed_elsewhere = true;
    } else {
        absorbed_elsewhere = ts[my_idx - 1].solved < n_problems;
    }
    if(absorbed_elsewhere) {
        balloons_left = 0;
    }
    if(balloons_left > 0) {
        me.penalty += 20 * (int64_t)balloons_left;
    }
    int rank = my_idx + 1;
    for(int i = my_idx - 1; i >= 0 && !better_than(ts[i], me); i--) {
        rank--;
    }
    return rank;
}

int low_pos(vector<TeamInfo> ts, int my_idx, int balloons_left) {
    int n = (int)ts.size();
    for(int i = my_idx - 1; i >= 0 && ts[i].rank == ts[my_idx].rank; i--) {
        if(i == 0 || ts[i - 1].rank != ts[my_idx].rank) {
            swap(ts[i], ts[my_idx]);
            my_idx = i;
            break;
        }
    }
    TeamInfo& me = ts[my_idx];
    int rank = me.rank;
    int i = my_idx + 1;
    while(balloons_left > 0 && i < n) {
        int j = i;
        while(i < n && ts[i].solved == ts[j].solved) {
            i++;
        }
        int det = me.solved - ts[j].solved;
        if(balloons_left < det) {
            break;
        }
        for(int l = j; l < i; l++) {
            for(int k = 0; k < det; k++) {
                accept_one(ts[l]);
            }
        }
        for(int l = j; balloons_left >= det && l < i; l++) {
            if(better_than(ts[l], me)) {
                balloons_left -= det;
                rank++;
            }
        }
        for(int l = j; balloons_left >= det + 1 && l < i; l++) {
            if(!better_than(ts[l], me) && !ts[l].rejects_unsolved.empty()) {
                balloons_left -= (det + 1);
                accept_one(ts[l]);
                rank++;
            }
        }
    }
    return rank;
}

void solve() {
    // This is purely an implementation problem: a careful parser plus a
    // greedy simulation, no clever algorithmic trick.
    //
    // Best (smallest) place: our team takes as many of its unsolved problems
    // as possible at minute 241, choosing the unsolved problem with the
    // fewest existing rejects first to minimize the added penalty. Any
    // leftover balloons are assumed absorbed by other teams without changing
    // our rank. The only case where they cannot be absorbed elsewhere is
    // when we are last AND every team above us has already solved every
    // problem: then each leftover balloon is forced onto our team as an
    // extra reject (+20 penalty). Once our state is fixed, the rank is
    // 1 + (# teams originally above us that still beat the new state). The
    // standings are sorted, so we scan upward from our position and stop at
    // the first beater.
    //
    // Worst (largest) place: keep our team's state untouched and try to push
    // as many of the teams currently below us past us. We first move our
    // team to the head of its rank-tie block so that tied teams below can
    // be made into overtakes through the +1-accept path. Then walk the
    // table in groups of equal solved count. A team in a group with
    // det = my_solved - their_solved fewer accepts overtakes us with either
    // det accepts (tied solved, win on penalty) or det + 1 accepts (strictly
    // out-solving us). Within each group we greedily count teams that
    // overtake at cost det, then teams that overtake at cost det + 1, until
    // balloons run out or no more groups are reachable. Each overtake adds
    // one to the rank.

    int my_idx = -1;
    for(int i = 0; i < (int)teams.size(); i++) {
        if(teams[i].name == my_team) {
            my_idx = i;
            break;
        }
    }
    int hp = high_pos(teams, my_idx, balloons);
    int lp = low_pos(teams, my_idx, balloons);
    cout << hp << ' ' << lp << '\n';
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    while(read()) {
        solve();
    }

    return 0;
}
```

---

## 4. Python solution with detailed comments

```python
import sys
from dataclasses import dataclass
from typing import List


@dataclass
class Team:
    # Team name.
    name: str

    # Number of solved problems in the frozen standings.
    solved: int

    # Frozen penalty.
    penalty: int

    # Frozen rank.
    rank: int

    # For every unsolved problem, store the number of previous wrong submissions.
    rejects_unsolved: List[int]


def better_than(a: Team, b: Team) -> bool:
    """
    Return True if team a is strictly better than team b.
    """
    if a.solved != b.solved:
        return a.solved > b.solved
    return a.penalty < b.penalty


def clone_teams(teams: List[Team]) -> List[Team]:
    """
    Make a deep-enough copy of teams.
    The only mutable field is rejects_unsolved, so copy that list.
    """
    return [
        Team(t.name, t.solved, t.penalty, t.rank, t.rejects_unsolved[:])
        for t in teams
    ]


def accept_one(t: Team) -> None:
    """
    Simulate one accepted problem for team t during the last hour.

    To minimize penalty, choose the currently unsolved problem with the fewest
    previous rejected submissions.
    """
    # Find index of cheapest unsolved problem.
    idx = min(range(len(t.rejects_unsolved)), key=lambda i: t.rejects_unsolved[i])

    # Number of previous rejected submissions on this problem.
    rejects = t.rejects_unsolved[idx]

    # One more solved problem.
    t.solved += 1

    # Earliest possible accepted submission is at minute 241.
    t.penalty += 241 + 20 * rejects

    # Remove this problem from the unsolved list.
    t.rejects_unsolved.pop(idx)


def parse_team_row(line: str, n_problems: int) -> Team:
    """
    Parse one standings row.

    Since team names may contain spaces, parsing is done from the right:
    problem results, penalty, solved count are all at the right side.
    """
    last = len(line) - 1

    # Skip trailing whitespace.
    while last >= 0 and line[last].isspace():
        last -= 1

    def skip_spaces():
        nonlocal last
        while last >= 0 and line[last].isspace():
            last -= 1

    def read_digits_back() -> int:
        nonlocal last
        value = 0
        base = 1

        while last >= 0 and line[last].isdigit():
            value += base * int(line[last])
            base *= 10
            last -= 1

        return value

    rejects_unsolved = []

    # Parse all problem result columns from right to left.
    for _ in range(n_problems):
        skip_spaces()

        # Optional number attached to '+' or '-' result.
        # For '.', this will usually be 0.
        num = read_digits_back()

        skip_spaces()

        # Result marker: '+', '-', or '.'
        sign = line[last]
        last -= 1

        # If the problem was not solved before freeze, it may be solved later.
        if sign != '+':
            rejects_unsolved.append(num)

    skip_spaces()

    # Parse frozen penalty.
    penalty = read_digits_back()

    skip_spaces()

    # Parse frozen solved count.
    solved = read_digits_back()

    # Now parse rank from the left.
    s = 0

    # Skip leading whitespace.
    while s < len(line) and line[s].isspace():
        s += 1

    rank = 0

    # Rank is the first integer in the row.
    while s < len(line) and line[s].isdigit():
        rank = rank * 10 + int(line[s])
        s += 1

    # Skip spaces after rank.
    while s < len(line) and line[s].isspace():
        s += 1

    # Trim trailing spaces before solved count.
    while last >= s and line[last].isspace():
        last -= 1

    # Everything between rank and solved-count area is the team name.
    name = line[s:last + 1]

    return Team(name, solved, penalty, rank, rejects_unsolved)


def is_team_row(line: str) -> bool:
    """
    A team row contains problem result symbols.
    The balloon-count line does not.
    """
    return any(c in ".+-" for c in line)


def high_pos(original_teams: List[Team], my_idx: int, balloons: int, n_problems: int) -> int:
    """
    Compute best possible rank for our team.
    """
    teams = clone_teams(original_teams)

    me = teams[my_idx]
    balloons_left = balloons

    # Give our team as many accepted problems as possible.
    while balloons_left > 0 and me.rejects_unsolved:
        accept_one(me)
        balloons_left -= 1

    n = len(teams)

    # Decide whether leftover balloons can be assigned elsewhere harmlessly.
    if my_idx != n - 1:
        absorbed_elsewhere = True
    elif my_idx == 0:
        absorbed_elsewhere = True
    else:
        absorbed_elsewhere = teams[my_idx - 1].solved < n_problems

    # If yes, they do not affect our rank.
    if absorbed_elsewhere:
        balloons_left = 0

    # Edge handling: unavoidable extra penalty.
    if balloons_left > 0:
        me.penalty += 20 * balloons_left

    # Initially assume every team above us still beats us.
    rank = my_idx + 1

    # Move up while teams above no longer beat us.
    i = my_idx - 1
    while i >= 0 and not better_than(teams[i], me):
        rank -= 1
        i -= 1

    return rank


def low_pos(original_teams: List[Team], my_idx: int, balloons: int) -> int:
    """
    Compute worst possible rank for our team.
    """
    teams = clone_teams(original_teams)
    n = len(teams)

    # Put our team at the beginning of its equal-rank block.
    i = my_idx - 1
    while i >= 0 and teams[i].rank == teams[my_idx].rank:
        if i == 0 or teams[i - 1].rank != teams[my_idx].rank:
            teams[i], teams[my_idx] = teams[my_idx], teams[i]
            my_idx = i
            break
        i -= 1

    me = teams[my_idx]

    # Start from frozen rank.
    rank = me.rank

    balloons_left = balloons

    # Start processing teams below us.
    i = my_idx + 1

    while balloons_left > 0 and i < n:
        j = i

        # Find a group with the same solved count.
        while i < n and teams[i].solved == teams[j].solved:
            i += 1

        # This group needs det solves to reach our solved count.
        det = me.solved - teams[j].solved

        # If not enough balloons to let even one team reach us, stop.
        if balloons_left < det:
            break

        # Simulate cheapest det solves for every team in this group.
        for l in range(j, i):
            for _ in range(det):
                accept_one(teams[l])

        # First, teams that beat us after exactly det solves.
        for l in range(j, i):
            if balloons_left >= det and better_than(teams[l], me):
                balloons_left -= det
                rank += 1

        # Then, teams that need one extra solve to beat us by solved count.
        for l in range(j, i):
            if balloons_left >= det + 1:
                if not better_than(teams[l], me) and teams[l].rejects_unsolved:
                    balloons_left -= det + 1
                    accept_one(teams[l])
                    rank += 1

    return rank


def solve_all() -> None:
    """
    Read all test cases and print answers.
    """
    lines = sys.stdin.read().splitlines()
    ptr = 0
    out = []

    def read_nonempty_line():
        nonlocal ptr

        while ptr < len(lines):
            line = lines[ptr]
            ptr += 1

            if any(not c.isspace() for c in line):
                return line

        return None

    while True:
        # Read header.
        header = read_nonempty_line()

        if header is None:
            break

        tokens = header.split()

        # Header has four fixed tokens before problem identifiers.
        n_problems = len(tokens) - 4

        teams = []

        # Read team rows until balloon count.
        while True:
            line = read_nonempty_line()

            if line is None:
                return

            if not is_team_row(line):
                balloons = int(line.strip())
                break

            teams.append(parse_team_row(line, n_problems))

        # Read our team name.
        my_team = read_nonempty_line()

        if my_team is None:
            return

        my_team = my_team.strip()

        # Find our team.
        my_idx = -1

        for idx, team in enumerate(teams):
            if team.name == my_team:
                my_idx = idx
                break

        # Compute best and worst ranks.
        best = high_pos(teams, my_idx, balloons, n_problems)
        worst = low_pos(teams, my_idx, balloons)

        out.append(f"{best} {worst}")

    print("\n".join(out))


if __name__ == "__main__":
    solve_all()
```

---

## 5. Compressed editorial

Parse each standings row from the right (team names may contain spaces). Store for each team: solved count, penalty, rank, and for each unsolved problem the number of previous rejects.

For the best rank: give our team as many last-hour accepts as possible (cheapest unsolved problem first, adding `241 + 20 * rejects`). Scan upward from our position and count teams that no longer beat us (stop at the first that does).

For the worst rank: move our team to the head of its tied-rank block. Process teams below us in groups by equal solved count. Let `det = my_solved - group_solved`; each team needs `det` or `det + 1` balloons to overtake us. Greedily count cheaper overtakes first. Return the number of overtakers + original rank.
