pro:给定N,M。输入N个物品,(si,vi)表示第i个物品体积为si,价值为vi,s<=300,vi<=1e9; N<1e6;现在要求,对于背包体积为1到M时,求出最大背包价值。

sol:显然直接跑背包会爆炸。 发现物品体积都比较小,我们先对相同体积的排序,对于体积相同的一起处理。

然后发现转移都是在差为体积整数倍之间,按照容量对体积取模分组 ,发现组内部转移满足神奇的决策单调性。 然后就是s次分治。

O(KClogC)

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep2(i,a,b) for(int i=b;i>=a;i--)
using namespace std;
const int maxn=310;
const int maxm=100010;
ll dp[2][maxm];
vector<ll>G[maxn]; int x,d,t;
void get(int L,int R,int l,int r)
{
    if(L>R) return ;
    int Mid=(L+R)>>1,pos=Mid;
    for(int i=min(r,Mid-1);i>=l;i--){
        if(Mid-i>G[x].size()) break;
        if(dp[t][d+Mid*x]<dp[t^1][d+i*x]+G[x][Mid-i-1]){
            pos=i; dp[t][d+Mid*x]=dp[t^1][d+i*x]+G[x][Mid-i-1];
        }
    }
    get(L,Mid-1,l,pos);
    get(Mid+1,R,pos,r);
}
int main()
{
    int N,M,s;ll v;
    scanf("%d%d",&N,&M);
    rep(i,1,N){
        scanf("%d%lld",&s,&v);
        G[s].push_back(v);
    }
    rep(i,1,300){
        if(G[i].size()==0) continue;
        sort(G[i].begin(),G[i].end(),greater<int>() );
        for(int j=1;j<G[i].size();j++) G[i][j]+=G[i][j-1];
        t^=1;  rep(j,1,M) dp[t][j]=dp[t^1][j];
        rep(j,0,i-1){
            d=j;  x=i;
            get(0,(M-j)/i,0,(M-j)/i);
        }
        rep(j,1,M) dp[t][j]=max(dp[t][j],dp[t][j-1]);
    }
    rep(i,1,M) printf("%lld ",dp[t][i]);
    return 0;
}