看似背包,实则一点也不背包,物品的数量为,每一个物品的体积是2^{b_{i} },所以体积一样的物品可以累加
然后一共有m个背包,求背包的最小体积,但可惜,也不是二分
正确的思路是从二进制和贪心的方向去想:
  • 二进制上,考虑每一位的贡献,而不是将打开
  • 二进制从大到小去想,1是因为如果先考虑小的体积,那么很有可能导致大的体积放不进去了,导致背包不充分填充;2是因为我们是考虑二进制每一位2^{b_{i} },如果当前有剩余,那么可以传递到下一位......
第一步:将二进制从大到小排序
bool cmp(pair<int, int> a, pair<int, int> b){
    return a.first > b.first;
}
int n, m; cin >> n >> m;    
map<int, int> ma;
for(int i = 1; i <= n; i ++){
    int a, b; cin >> a >> b;
    ma[b] += a;
}
vector<pair<int, int> > a;
for(auto [v, w] : ma) a.push_back({v, w});
sort(a.begin(), a.end(), cmp); 

第二步:用记录当前可以使用的的个数,表示上一个2^{b_{i+1}},因为从2^{b_{i+1}}中间差了记录答案
  • 剪枝:在从大到小循环的过程中,会不断的乘上,那么可能会超时范围,思考:当当前的大于2^{50}时,一定可以满足将后面的所有物品都装入背包中,所有剪枝:
表示的是当前的的次方,的意思是2^{b_{i+1}}中间差了就等于
if(total){
    int cnt = 0, now_total = total;
    while(now_total){
        cnt ++;
        now_total >>= 1;
    }
    if(cnt + pre - v > 50) break;
    total *= ksm(2, pre - v, 1000000000000000000);
}
然后如果当前的大,那么就用掉个;否则,对于每一个背包,我需要添加容积为2^{v}的个数是,所以累加答案,然后为用剩下的空间:
if(w <= total) total -= w;
else{
    w -= total;
    int x = (w + m - 1) / m;
    ans = (ans + ((x % mod) * ksm(2, v, mod)) % mod) % mod;
    total = x * m - w;
} 

总代码:
#include<bits/stdc++.h>
using namespace std;

#define endl '\n'
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define HelloWorld IOS;

const int mod = 998244353;


bool cmp(pair<int, int> a, pair<int, int> b){
    return a.first > b.first;
}

int ksm(int a, int b, int p){
    int sum = 1;
    while(b){
        if(b & 1) sum = (sum * a) % p;
        b >>= 1;
        a = (a * a) % p;
    }
    return sum;
}

void solve(){

    int n, m; cin >> n >> m;
    map<int, int> ma;
    for(int i = 1; i <= n; i ++){
        int a, b; cin >> a >> b;
        ma[b] += a;
    }
    vector<pair<int, int> > a;
    for(auto [v, w] : ma) a.push_back({v, w});
    sort(a.begin(), a.end(), cmp);
    int total = 0, pre = a[0].first, ans = 0;

    for(int i = 0; i < (int)a.size(); i ++){
        int v = a[i].first, w = a[i].second;
        if(total){
            int cnt = 0, now_total = total;
            while(now_total){
                cnt ++;
                now_total >>= 1;
            }
            if(cnt + pre - v > 50) break;
            total *= ksm(2, pre - v, 1000000000000000000);
        }
        pre = v;
        if(w <= total) total -= w;
        else{
            w -= total;
            int x = (w + m - 1) / m;
            ans = (ans + ((x % mod) * ksm(2, v, mod)) % mod) % mod;
            total = x * m - w; 
        }
    }
    cout << ans << endl;
    return ;
}

signed main(){
    HelloWorld;

    int tt = 1; 
    cin >> tt;
    while(tt --) solve();
    return 0;
}