LibreOJ #6039.「雅礼集训 2017 Day5」珠宝 dp+决策单调性+分治

题意

n 件物品,每件物品有一个价格 c i 和价值 v i ,现在对于所有的 1 i k ,求出用 i 元最多可以得到多大价值的物品。
n 1000000 , k 50000 , 1 c i 300 , 1 v i 10 9

分析

其实就是一个背包问题。
我们可以利用 c i 很小的性质,设 f i , j 表示价格不大于 i 的所有物品,花了 j 元能得到的最大价值。
假如知道了选的物品数 s ,那么肯定会选择价值最大的 s 件物品。
如果暴力枚举 s 的话,复杂度是 O ( c k 2 ) ,不难注意到这个dp有决策单调性,那么可以通过分治来做到 O ( c k l o g k ) .

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>

typedef long long LL;

const int N=50005;

int n,m,a[N],tot,mx;
LL f[305][N];
std::vector<LL> w[305];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

bool cmp(int x,int y)
{
    return x>y;
}

void solve(int l,int r,int L,int R,int id)
{
    if (l>r) return;
    int mid=(l+r)/2,pos=L;
    for (int i=std::max(L,mid-(int)w[id].size());i<=std::min(mid,R);i++)
        if (f[id-1][a[i]]+(i==mid?0:w[id][mid-i-1])>f[id][a[mid]])
        {
            f[id][a[mid]]=f[id-1][a[i]]+(i==mid?0:w[id][mid-i-1]);
            pos=i;
        }
    solve(l,mid-1,L,pos,id);solve(mid+1,r,pos,R,id);
}

int main()
{
    n=read();m=read();
    for (int i=1;i<=n;i++)
    {
        int x=read(),y=read();
        mx=std::max(mx,x);
        w[x].push_back(y);
    }
    for (int i=1;i<=mx;i++)
    {
        std::sort(w[i].begin(),w[i].end(),cmp);
        for (int j=1;j<w[i].size();j++) w[i][j]+=w[i][j-1];
    }
    for (int i=1;i<=mx;i++)
        for (int j=0;j<i;j++)
        {
            tot=0;
            for (int k=j;k<=m;k+=i) a[++tot]=k;
            solve(1,tot,1,tot,i);
        }
    for (int i=1;i<=m;i++) printf("%lld ",f[mx][i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33229466/article/details/80778071