Need For Brake (Codeforces 73B)
Tags: Greedy, binary search

Vasya plays the Need For Brake. He plays because he was presented with a new computer wheel for birthday! Now he is sure that he will win the first place in the championship in his favourite racing computer game! n racers take part in the championship, which consists of a number of races. After each race racers are arranged from place first to n-th (no two racers share the same place) and first m places are awarded. Racer gains bi points for i-th awarded place, which are added to total points, obtained by him for previous races. It is known that current summary score of racer i is ai points. In the final standings of championship all the racers will be sorted in descending order of points. Racers with an equal amount of points are sorted by increasing of the name in lexicographical order.Unfortunately, the championship has come to an end, and there is only one race left. Vasya decided to find out what the highest and lowest place he can take up as a result of the championship.

Input: The first line contains number n (1 ≤ n ≤ 105) — number of racers. Each of the next n lines contains si and ai — nick of the racer (nonempty string, which consist of no more than 20 lowercase Latin letters) and the racer's points (0 ≤ ai ≤ 106). Racers are given in the arbitrary order.The next line contains the number m (0 ≤ m ≤ n). Then m nonnegative integer numbers bi follow. i-th number is equal to amount of points for the i-th awarded place (0 ≤ bi ≤ 106).The last line contains Vasya's racer nick.

Output: Output two numbers — the highest and the lowest place Vasya can take up as a result of the championship.


Sample Inputs:
3
teama 10
teamb 20
teamc 40
2
10 20
teama

2
teama 10
teamb 10
2
10 10
teamb

Sample Outputs:
2 3

2 2

Solution (Python):
class Racer:

    def __init__(self, name, points):
        self.name = name
        self.points = points

    def __str__(self):
        return '%s %d' % (self.name, self.points)
n = int(input())
best = n * [None]
worst = n * [None]
for i in range(n):
    (name, points) = input().split()
    points = int(points)
    best[i] = Racer(name, points)
    worst[i] = Racer(name, points)
m = int(input())
place_bonus = list(reversed(sorted(map(int, input().split()))))
your_name = input().strip()
debugging = False

def debug_print(s):
    if debugging:
        print(s)

def find(racers, name):
    for i in range(n):
        if racers[i].name == name:
            return i

def sort_racers(racers):
    racers.sort(key=lambda racer: (-racer.points, racer.name))
you = best[find(best, your_name)]
if m != 0:
    you.points += place_bonus[0]
sort_racers(best)
bonus_pos = 1
for i in range(find(best, your_name) - 1, -1, -1):
    if bonus_pos >= m:
        break
    best[i].points += place_bonus[bonus_pos]
    bonus_pos += 1
for i in range(n - 1, -1, -1):
    if bonus_pos >= m:
        break
    best[i].points += place_bonus[bonus_pos]
    bonus_pos += 1
sort_racers(best)
debug_print(list(map(str, best)))
best_ranking = find(best, your_name) + 1
debug_print('best ranking: %d' % best_ranking)
you = worst[find(worst, your_name)]
if m == n:
    you.points += place_bonus.pop()
    m -= 1
sort_racers(worst)
bonus_pos = m - 1
worst_pos = find(worst, your_name) + 1
while bonus_pos >= 0 and worst_pos < n:
    debug_print('bonus_pos = %d, worst_pos = %d' % (bonus_pos, worst_pos))
    candidate = worst[worst_pos]
    need = you.points - candidate.points
    if candidate.name > you.name:
        need += 1
    if place_bonus[bonus_pos] >= need:
        candidate.points += place_bonus[bonus_pos]
        worst_pos += 1
    bonus_pos -= 1
sort_racers(worst)
debug_print(list(map(str, worst)))
worst_ranking = find(worst, your_name) + 1
debug_print('worst ranking: %d' % worst_ranking)
print('%d %d' % (best_ranking, worst_ranking))

Solution (Python):
class Racer:

    def __init__(self, name, points):
        self.name = name
        self.points = points

    def __str__(self):
        return '%s %d' % (self.name, self.points)
n = int(input())
best = n * [None]
worst = n * [None]
for i in range(n):
    (name, points) = input().split()
    points = int(points)
    best[i] = Racer(name, points)
    worst[i] = Racer(name, points)
m = int(input())
place_bonus = list(reversed(sorted(map(int, input().split()))))
your_name = input().strip()
debugging = False

def debug_print(s):
    if debugging:
        print(s)

def find(racers, name):
    for i in range(n):
        if racers[i].name == name:
            return i

def sort_racers(racers):
    racers.sort(key=lambda racer: (-racer.points, racer.name))
you = best[find(best, your_name)]
if m != 0:
    you.points += place_bonus[0]
sort_racers(best)
bonus_pos = 1
for i in range(find(best, your_name) - 1, -1, -1):
    if bonus_pos >= m:
        break
    best[i].points += place_bonus[bonus_pos]
    bonus_pos += 1
for i in range(n - 1, -1, -1):
    if bonus_pos >= m:
        break
    best[i].points += place_bonus[bonus_pos]
    bonus_pos += 1
sort_racers(best)
debug_print(list(map(str, best)))
best_ranking = find(best, your_name) + 1
debug_print('best ranking: %d' % best_ranking)
you = worst[find(worst, your_name)]
if m == n:
    you.points += place_bonus.pop()
    m -= 1
sort_racers(worst)
bonus_pos = m - 1
worst_pos = find(worst, your_name) + 1
while bonus_pos >= 0 and worst_pos < n:
    debug_print('bonus_pos = %d, worst_pos = %d' % (bonus_pos, worst_pos))
    candidate = worst[worst_pos]
    need = you.points - candidate.points
    if candidate.name > you.name:
        need += 1
    if place_bonus[bonus_pos] >= need:
        candidate.points += place_bonus[bonus_pos]
        worst_pos += 1
    bonus_pos -= 1
sort_racers(worst)
debug_print(list(map(str, worst)))
worst_ranking = find(worst, your_name) + 1
debug_print('worst ranking: %d' % worst_ranking)
print('%d %d' % (best_ranking, worst_ranking))

Solution (Python):
class Racer:

    def __init__(self, name, points):
        self.name = name
        self.points = points

    def __str__(self):
        return '%s %d' % (self.name, self.points)
n = int(input())
best = n * [None]
worst = n * [None]
for i in range(n):
    (name, points) = input().split()
    points = int(points)
    best[i] = Racer(name, points)
    worst[i] = Racer(name, points)
m = int(input())
place_bonus = list(reversed(sorted(map(int, input().split()))))
your_name = input().strip()
debugging = False

def debug_print(s):
    if debugging:
        print(s)

def find(racers, name):
    for i in range(n):
        if racers[i].name == name:
            return i

def sort_racers(racers):
    racers.sort(key=lambda racer: (-racer.points, racer.name))
you = best[find(best, your_name)]
if m != 0:
    you.points += place_bonus[0]
sort_racers(best)
bonus_pos = 1 + find(best, your_name)
for i in range(n - 1, -1, -1):
    if bonus_pos >= m:
        break
    best[i].points += place_bonus[bonus_pos]
    bonus_pos += 1
sort_racers(best)
debug_print(list(map(str, best)))
best_ranking = find(best, your_name) + 1
debug_print('best ranking: %d' % best_ranking)
you = worst[find(worst, your_name)]
if m == n:
    you.points += place_bonus.pop()
    m -= 1
sort_racers(worst)
bonus_pos = m - 1
worst_pos = find(worst, your_name) + 1
while bonus_pos >= 0 and worst_pos < n:
    debug_print('bonus_pos = %d, worst_pos = %d' % (bonus_pos, worst_pos))
    candidate = worst[worst_pos]
    need = you.points - candidate.points
    if candidate.name > you.name:
        need += 1
    if place_bonus[bonus_pos] >= need:
        candidate.points += place_bonus[bonus_pos]
        worst_pos += 1
    bonus_pos -= 1
sort_racers(worst)
debug_print(list(map(str, worst)))
worst_ranking = find(worst, your_name) + 1
debug_print('worst ranking: %d' % worst_ranking)
print('%d %d' % (best_ranking, worst_ranking))