題目描述
從前有一個奇怪的商店,一共售賣k種物品,第i種物品的初始價格爲i。
但是這商店有個很奇怪的規矩,就是你每次購買一樣物品之後,這種物品的價格都會在當前基礎上翻一倍。
現在我們想要用最少的錢從裏面買n樣物品,不限購買的物品種數和每種物品購買的次數,請求出若這樣做,所買到的最貴的物品的價格,由於這個數字可能過大,你只需要輸出其模1000000007=10^9+7的結果即可。
輸入
每個測試點第一行一個整數T,表示數據組數。
接下來T行,每行兩個正整數n,k。
輸出
T行,第i行表示第i組數據的答案。
樣例輸入 Copy
5
3 1
3 2
5 3
1000000000 1
987654321 876543210
樣例輸出 Copy
4
2
4
570312504
493827168
提示
對於100%的數據,1≤T≤2000,1≤n,k≤10^9。
分兩種情況;
1.答案小於等於k
2.答案大於k
對於第一種情況,直接二分答案,對於每一個二分的答案x,計算,這部分採用分塊加速即可
對於第二種情況,首先算出價格爲k時所能買到的物品數量爲mx,那麼剩餘需要買的數量爲num1=n-mx,因爲1~n這些數*2的比n大的數在[n+1,2*n]這個範圍裏直接對應,並且這個[n+1,2*n]出現的數爲n個數,對於[2*n+1,4*n]這個範圍也是如此,並且每一個數之間的大小關係不發生變化,所以,我們只要求出有num1/n個這樣的完整區間,然後剩下的num1%n在[n/2+1,n]區間中找即可。
複雜度爲(t*sqrt(k)*log(k))
update:至於爲啥在[n/2+1,n]這個區間找是因爲如果在[1,n/2]這個區間,裏面任何數的2倍還是在[1,n]之間
/**/
#pragma GCC optimize(3,"Ofast","inline")
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <stack>
#include <queue>
typedef long long LL;
using namespace std;
const long long mod = 1e9 + 7;
int t, n, k;
LL mx;
int check(int x){
LL ans = 0;
for (int i = 1, j; i <= x; i = j + 1){
j = x / (x / i);
ans += ((LL)std::log2(1.0 * x / i) + 1) * (j - i + 1);
}
mx = max(mx, ans);
return ans >= k;
}
int get(int x){
LL ans = 0;
for (int i = 1, j; i <= x; i = j + 1){
j = x / (x / i);
ans += ((LL)std::log2(1.0 * x / i) + 1) * (j - i + 1);
}
return ans;
}
int check1(int x, int num){
LL ans = 0;
for (int i = 1, j; i <= x; i = j + 1){
j = x / (x / i);
ans += ((LL)std::log2(1.0 * x / i) + 1) * (j - i + 1);
}
return ans >= num;
}
LL poww(LL x, LL num){
LL res = 1;
while(num){
if(num & 1) res = res * x % mod;
x = x * x % mod;
num >>= 1;
}
return res;
}
int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
scanf("%d", &t);
while(t--){
mx = 0;
scanf("%d %d", &k, &n);
if(!check(n)){
int num = get(n >> 1);
int num1 = k - mx;
int num2 = num1 / n;
num1 %= n;
if(num1 == 0) num1 = n, num2--;
int l = (n >> 1), r = n, ans = n;
while(l <= r){
int mid = (l + r) >> 1;
if(check1(mid, num1 + num)) ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%lld\n", (ans << 1) * poww(2, num2) % mod);
continue;
}
int l = 0, r = n, ans = n;
while(l <= r){
int mid = (l + r) >> 1;
if(check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%d\n", ans);
}
return 0;
}
/*
6
3 1
3 2
5 3
1000000000 1
987654321 876543210
6 3
*/