package main

type Queue interface {
	Enqueue(value int)
	Dequeue() int
	Peek() int
	IsEmpty() bool
}

type MonoDecQueue struct {
	elements []int
}

func NewMonoDecQueue() *MonoDecQueue {
	return &MonoDecQueue{elements: make([]int, 0)}
}

func (q *MonoDecQueue) Enqueue(value int) {
	for !q.IsEmpty() && q.elements[len(q.elements)-1] < value {
		q.elements = q.elements[:len(q.elements)-1]
	}
	q.elements = append(q.elements, value)
}

func (q *MonoDecQueue) Dequeue() int {
	if q.IsEmpty() {
		panic("queue is empty")
	}
	value := q.elements[0]
	q.elements = q.elements[1:]
	return value
}

func (q *MonoDecQueue) Peek() int {
	if q.IsEmpty() {
		panic("queue is empty")
	}
	return q.elements[0]
}

func (q *MonoDecQueue) IsEmpty() bool {
	return len(q.elements) == 0
}

func MaximumRobots(chargeTimes []int, runningCosts []int, budget int64) int {
	l := len(chargeTimes)
	if l != len(runningCosts) {
		return 0
	}

	j := 0
	prevJ := -1
	s := 0
	maxRobots := 0

	q := NewMonoDecQueue()

	for i := 0; i < l; i++ {

		if j < i {
			j = i
		}

		for ; j < l; j++ {
			num := j - i + 1
			if prevJ != j {
				s += runningCosts[j]
				q.Enqueue(chargeTimes[j])
			}

			prevJ = j
			if int64(s*num+q.Peek()) <= budget {
				if maxRobots <= num {
					maxRobots = num
				}
			} else {
				break
			}
		}

		if !q.IsEmpty() {
			if q.Peek() == chargeTimes[i] {
				q.Dequeue()
			}
		}

		s -= runningCosts[i]
	}

	return maxRobots
}

func MaximumRobotsEx(chargeTimes []int, runningCosts []int, budget int64) int {
	l := len(chargeTimes)
	if l != len(runningCosts) {
		return 0
	}

	prevCharges := make([]int, l)
	prevCosts := make([]int, l)
	maxWndRobots := make([]int, l+1)

	maxRobots := 0

	for wnd := 1; wnd <= l; wnd++ {
		windows := l - wnd + 1

		begin := 0
		end := wnd - 1
		w := 0
		for w = 0; w < windows; w++ {
			m := chargeTimes[end]

			if m > prevCharges[w] {
				prevCharges[w] = m
			}
			prevCosts[w] += runningCosts[end]

			totalCost := int64(wnd*prevCosts[w] + prevCharges[w])
			if totalCost <= budget {
				maxRobots = wnd
			}

			begin++
			end++
		}

		maxWndRobots[wnd] = maxRobots
		if maxWndRobots[wnd] == maxWndRobots[wnd-1] {
			break
		}
	}

	return maxRobots
}
