商店(分塊+二分)

題目描述

從前有一個奇怪的商店,一共售賣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,計算\sum_{i=1}^{x}(log2(\frac{x}{i}) +1),這部分採用分塊加速即可

對於第二種情況,首先算出價格爲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
*/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章