题意
\(n\) 个物品,体积 \(v_i\) 价值 \(w_i\),做 01 背包,\(n\le 10^6,m\le 5\times10^4,v_i\le 300\)。
原题忘了叫啥了。
分析
发现 \(v_i\) 非常小,考虑把物品按照体积分类,逐类处理。
对于体积为 \(i\) 的物品,我们肯定要按照价值从大到小取。将这些物品排序做前缀和,设选前 \(i\) 大的数的和为 \(s_i\),则 \(s_i\) 是上凸的。
显然有转移 \(f_j=\max(f_j,f_{j-k\times i}+s_k)\)。不难发现转移中下标模 \(i\) 不同的状态是独立的,所以我们可以考虑按照模 \(i\) 的余数进一步分类处理,那么转移方程可以写成 \(f'_j=\max(f'_j,f'_{j-k}+s_k)\),由于 \(s_k\) 是一个上凸函数,其满足四边形不等式“交叉大于包含”,所以 \(f'_j\) 的转移具有决策单调性(简证:\(s_{k+1}-s_k<s_k-s_{k-1}\rightarrow 2s_k>s_{k-1}+s_{k+1}\)),可以分治解决。时间复杂度 \(O(mv\log m)\)。
代码:
int n,m;
int k;
i64 f[maxm],g[maxm],h[maxm];
vector<int>v[maxn];
vector<i64>s;
void solve(int l,int r,int L,int R,const int id){if(l>r)return;int mid=(l+r)>>1;i64 res=0;int p=mid;rep(i,max(L,mid-(int)v[id].size()),min(R,mid-1)){i64 ret=g[i]+s[mid-i-1];if(ret>res)res=ret,p=i;}h[mid]=res;if(l==r)return;solve(l,mid,L,p,id),solve(mid+1,r,p,R,id);
}
inline void solve_the_problem(){n=rd(),m=rd();rep(i,1,n){int x=rd(),y=rd();v[x].emplace_back(y);}rep(i,1,w)if(!v[i].empty()){sort(all(v[i]),greater<int>());s.assign(v[i].size(),0);s[0]=v[i][0];rep(j,1,(int)v[i].size()-1)s[j]=s[j-1]+v[i][j];rep(d,0,i-1){k=0;reprange(j,d,m,i)g[++k]=f[j];solve(1,k,1,k,i);rep(j,1,k)ckmx(f[(j-1)*i+d],h[j]);}}rep(i,1,m)ckmx(f[i],f[i-1]);rep(i,1,m)write(f[i],32);
}