Problem:
Score : 700 points

Problem Statement

We have a rooted binary tree with N vertices, where the vertices are numbered 1 to N.
Vertex 1 is the root, and the parent of Vertex i (i \geq 2) is Vertex \left[ \frac{i}{2} \right].

Each vertex has one item in it. The item in Vertex i has a value of V_i and a weight of W_i.
Now, process the following query Q times:

Given are a vertex v of the tree and a positive integer L.
Let us choose some (possibly none) of the items in v and the ancestors of v so that their total weight is at most L.
Find the maximum possible total value of the chosen items.

Here, Vertex u is said to be an ancestor of Vertex v when u is an indirect parent of v, that is, there exists a sequence of vertices w_1,w_2,\ldots,w_k (k\geq 2) where w_1=v, w_k=u, and w_{i+1} is the parent of w_i for each i.

Constraints

All values in input are integers.

1 \leq N < 2^{18}

1 \leq Q \leq 10^5

1 \leq V_i \leq 10^5

1 \leq W_i \leq 10^5

For the values v and L given in each query, 1 \leq v \leq N and 1 \leq L \leq 10^5.

Input

Let v_i and L_i be the values v and L given in the i-th query.
Then, Input is given from Standard Input in the following format:

N
V_1 W_1
:
V_N W_N
Q
v_1 L_1
:
v_Q L_Q

Output

For each integer i from 1 through Q,
the i-th line should contain the response to the i-th query.

Sample Input 1

3
1 2
2 3
3 4
3
1 1
2 5
3 5

Sample Output 1

0
3
3

In the first query, we are given only one choice: the item with (V, W)=(1,2). Since L = 1, we cannot actually choose it, so our response should be 0.

In the second query, we are given two choices: the items with (V, W)=(1,2) and (V, W)=(2,3). Since L = 5, we can choose both of them, so our response should be 3.

Sample Input 2

15
123 119
129 120
132 112
126 109
118 103
115 109
102 100
130 120
105 105
132 115
104 102
107 107
127 116
121 104
121 115
8
8 234
9 244
10 226
11 227
12 240
13 237
14 206
15 227

Sample Output 2

256
255
250
247
255
259
223
253

Solution in Python (3.8.2) (judge verdict: Time Limit Exceeded)

import numpy as np
#from pandas import DataFrame
import copy

class BranchAndBound:
    def __init__(self, weights, values, max_weight, answer_val):
        self.weights = np.array(weights)
        self.values = np.array(values)
        self.max_weight = max_weight
        self.answer_val = answer_val
        self.evaluates = self.values/self.weights
        self.index_list = []
        for index in range(1, len(weights) + 1):
#            self.index_list.append('x' + str(index))
             self.index_list.append(int(index)-1)
#        self.target_df = DataFrame(np.c_[self.evaluates, self.weights, self.values, np.array(self.answer_val)], index=self.index_list, columns=["evaluate", "weight", "value", "ans"])
#        self.target_df = self.target_df.sort_values('evaluate', ascending=False)
#        self.answer_val = list(self.target_df['ans']) #　answer_val　を評価値の高い順にソートされたものに変更
#        del self.target_df['ans'] # DataFrameにはもう必要ないので「ans」カラムを削除
        self.target_array = np.c_[self.evaluates, self.weights, self.values, np.array(self.answer_val)]
        self.target_array = self.target_array[np.argsort(self.target_array[:,0])[::-1],:]
        self.answer_val = self.target_array[:,3].tolist() #　answer_val　を評価値の高い順にソートされたものに変更
        self.target_array = self.target_array[:,:3]
        
        self.target_ans = np.dot(np.array(answer_val), values)
#        self.index_list = self.target_df.index #indexの順番変更
        self.index_list = np.argsort(self.target_array[:,0])[::-1] #indexの順番変更

    def __judgeValue(self, fixed_list): # fixed_listで渡されたxの固定値における緩和問題を解き、分岐継続を判定する。また、よりよい解が見つかれば暫定解の交換を行う。
        sum_weight = 0 # 採択したxのweightの合計値
        evaluate_list = [] # 採択の判定を格納
        evaluate_list.extend(fixed_list)
        for index, val in enumerate(fixed_list):
            sum_weight += self.target_array[self.index_list[index],1]*val #  fixed_listで渡されたxの値でのweightの合計値

        for index in range(len(fixed_list), len(self.index_list)):
            if sum_weight > self.max_weight: #max_weightを超えた場合は分岐終了
                return False # 分岐終了
            elif sum_weight == self.max_weight: #max_weightに達しているので他のxは0
                evaluate_list.append(0)
                continue
            else:
                if sum_weight + self.target_array[self.index_list[index],1] < self.max_weight: # x=1にしてもmax_weightに到達しないとき
                    sum_weight += self.target_array[self.index_list[index],1]
                    evaluate_list.append(1)
                else: # 0 < x <= 1 となるとき
                    evaluate_list.append((self.max_weight - sum_weight)/self.target_array[self.index_list[index],1])
                    sum_weight = self.max_weight
                    if (self.max_weight - sum_weight) == self.target_array[self.index_list[index],1]: # x=1のとき、暫定解を入れ替える
                        evaluate_list_count = len(evaluate_list)
                        for i in range(evaluate_list_count, len(self.index_list)): # 決まっていないxは全部0を入れる
                            evaluate_list.append(0)
                        self.target_ans = np.dot(np.array(evaluate_list), self.target_array[:,2]) # 暫定解 target_ansの入れ替え　
                        self.answer_val = evaluate_list # 暫定解 answer_valの入れ替え
                        return False # 分岐終了

        if len(fixed_list) == len(self.index_list): # 全てのxの値が固定されているとき 暫定解との比較
            if (sum_weight <= self.max_weight) and (np.dot(np.array(fixed_list), self.target_array[:,2]) > self.target_ans): # 暫定解との比較
                self.target_ans = np.dot(np.array(fixed_list), self.target_array[:,2]) # 暫定解 target_ansの入れ替え　
                self.answer_val = fixed_list # 暫定解 answer_valの入れ替え
            return False

        if np.dot(np.array(evaluate_list), self.target_array[:,2]) > self.target_ans: # 緩和問題の解が暫定解を超えた時
            return True # 分岐継続
        else: # 暫定解を超えていないとき
            return False # 分岐終了 

    def breadthFirstSearch(self): #幅優先探索
        search_lists = [[0], [1]] # 要素 [0]、[1]は先に入れておく
        while len(search_lists) != 0: # search_listsが空になるまで続ける
            first_list = search_lists[0] # Queueで考える、上から１つ取得
            search_lists.pop(0) # 取得した要素は削除
            if self.__judgeValue(first_list): # 探索が継続かどうか
                new_list_cp = copy.deepcopy(first_list) # 次要素に「1」を追加するために深いコピー
                new_list_cp.append(0) # 0を末尾に追加
                search_lists.append(new_list_cp) # 新たな要素を search_listsの末尾に格納
                new_list_cp = copy.deepcopy(first_list) # 次要素に「0」を追加するために深いコピー
                new_list_cp.append(1) # 1を末尾に追加
                search_lists.append(new_list_cp) # 新たな要素を　search_listsの末尾に格納

#        print("-----幅優先探索-----")
#        for index, val in enumerate(self.index_list):
#            print(val + ": " + str(self.answer_val[index]))
#        print("ans: " + str(self.target_ans))
        return int(self.target_ans+0.1)
        

    def depthFirstSearch(self): #深さ優先探索
        search_lists = [[0], [1]] # 要素 [0]、[1]は先に入れておく
        while len(search_lists) != 0: # search_listsが空になるまで続ける
            first_list = search_lists[0] # Stachで考える、上から１つ取得
            search_lists.pop(0) # 取得した要素は削除
            if self.__judgeValue(first_list): # 探索が継続かどうか
                new_list_cp = copy.deepcopy(first_list) # 次要素に「1」を追加するために深いコピー
                new_list_cp.append(1) # 1を末尾に追加
                search_lists.insert(0, new_list_cp) # 新たな要素を search_listsの先頭に格納
                new_list_cp = copy.deepcopy(first_list) # 次要素に「0」を追加するために深いコピー
                new_list_cp.append(0) # 0を末尾に追加
                search_lists.insert(0, new_list_cp) # 新たな要素を　search_listsの先頭に格納

#        print("-----深さ優先探索-----")
#        for index, val in enumerate(self.index_list):
#            print(val + ": " + str(self.answer_val[index]))
#        print("ans: " + str(self.target_ans))
        return int(self.target_ans+0.1)


def Parent(v):
    parent = [v]
    c = v
    while c>1:
        p = int((c+0.1)//2)
        parent.append(p)
        c = p
    return parent


#f = open('./input_D.txt')
#N = int(f.readline())
N = int(input())
V = []
W = []
for i in range(N):
#    Vi,Wi = map(int, f.readline().split())
    Vi,Wi = map(int, input().split())
    V.append(Vi)
    W.append(Wi)
    
#Q = int(f.readline())
Q = int(input())
for i in range(Q):
#    v,L = map(int, f.readline().split())
    v,L = map(int, input().split())
    parent = Parent(v)
    Vparent = []
    Wparent = []
    for p in parent:
        Vparent.append(V[p-1])
        Wparent.append(W[p-1])
    bb = BranchAndBound(Wparent,Vparent, L, [0 for _ in range(len(parent))] )
    ans = bb.breadthFirstSearch()
    print(str(int(ans)))