2025ICPC武汉邀请赛-F
题意如下图
思路:
考虑一种贪心策略,
假设最大的物品体积为2^v,那么我每个背包都至少要有2^v的体积,那么假设有m个背包,我的容量就增加了m*2^v,如果此时最大的物品有k个,那么现在可以支配给后面小物品的体积就就有(m-k)*2^v也就是(m-k)个2^v,但是这个可支配体积难以表示,所以可以逐位考虑,如果在2^v对应的这个位置有a个可以支配给后面物品的体积,那么在2^(v-1)对应的位置就有2*a个可以支配给后面物品的体积,这样逐位计算,可以做到不用计算体积也能把体积分配下去
用一个简单的图示可以表示这种做法
如图1:2^i下面的1的个数对应物品这一位对应物品个数(这个样例k也就是背包数量为4)
此时只能把背包容量从0扩到2^4
图2,'-'表示背包扩容之后可以支配的体积,用了3个,还剩一个可支配的体积,乘2给到下一位
图三,刚好抵掉2^3这一位
图四:背包容量从2^4扩容到2^4+2^2,多四个2^2的可支配的体积,用了三个剩一个
图五:乘二
图六:乘二刚好抵掉,所以这个样例答案为2^4+2^2=20
按照这个思路,可以初步实现代码,但是还有一种情况就是2^m级别的体积向下转移到2^(m-1e9),也就是如果可以支配的有剩余,就会不断*2转移到下一位,但是根据数据范围可以发现可能会出现转移1e9次的情况,这种情况可以发现,转移32次以上,可支配的体积已经达到了2*32级别,那么可支配的体积一定容得下剩余的体积,于是提前结束循环得出答案即可
但是要注意,初始的某一位的物品个数如果超过了背包个数k,那么多余部分要除以二向高位转移,确保每一位的物品个数<=k
代码实现:
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define u1 (u<<1)
#define u2 (u<<1|1)
#define pb push_back
#define pp pop_back()
#define int long long
#define laile cout<<"laile"<<endl
#define lowbit(x) ((x)&(-x))
#define double long double
#define sf(x) scanf("%lld",&x)
#define sff(x,y) scanf("%lld %lld",&x,&y)
#define sd(x) scanf("%Lf",&x)
#define sdd(x,y) scanf("%Lf %Lf",&x,&y)
#define _for(i,n) for(int i=0;i<(n);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _pre(i,a,b) for(int i=(a);i>=(b);--i)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef unsigned long long ULL;
typedef pair<int,int>PII;
const int N=6e6+10,INF=4e18,P=998244353;
int n,m;
int qmi(int a,int b,int P){int res=1;while(b){if(b&1)res=res*a%P;a=a*a%P;b>>=1;}return res;
}
vector<PII>v(N);
void solve()
{map<int,int>mp;cin>>n>>m;_rep(i,1,n){int a,b;cin>>a>>b;mp[b]+=a;}int idx=0;for(auto i:mp){if(i.se>m){int sub=i.se-m;mp[i.fi]-=sub;if(sub&1){sub++;mp[i.fi]--;}mp[i.fi+1]+=sub/2;}if(mp[i.fi]){v[++idx]={i.fi,mp[i.fi]};}}int pre=-1,res=0,fu=0;_pre(i,idx,1){if(pre==-1){res+=qmi(2,v[i].fi,P);fu=m-v[i].se;}else{if(fu&&pre-v[i].fi>40){cout<<res<<'\n';return ;}else{if(fu){for(int j=pre;j>v[i].fi;j--){//2^jfu*=2;if(fu>2*m){cout<<res<<'\n';return ;}}}if(fu>=v[i].se)fu-=v[i].se;else {v[i].se-=fu;//剩res=res+qmi(2,v[i].fi,P);res%=P;fu=m-v[i].se;}}}pre=v[i].fi;}cout<<res<<'\n';return ;
}
signed main()
{IOS;int T=1;cin>>T;while(T--)solve();return 0;
}