• Loj#6039「雅礼集训 2017 Day5」珠宝【四边形不等式,dp】


    正题

    题目链接:https://loj.ac/p/6039


    题目大意

    \(n\)个物品,第\(i\)个费用为\(w_i\),价值为\(v_i\),对于\(k\in[1,m]\)求费用为\(m\)时能获得的最大价值。

    \(1\leq n\leq 10^6,1\leq m\leq 5\times 10^4,1\leq w_i\leq 300,1\leq v_i\leq 10^9\)


    解题思路

    好早以前写的不过不知道为啥错了,现在来补个新的。

    \(w_i\)很小,考虑以其为突破口,显然地我们可以把\(w_i\)相同的按照\(v_i\)从大到小排序,那么对于每个\(w_i\),我们就可以选择若干个。

    \(f_{i,j}\)表示做到\(w=i\)时费用为\(j\)的最大价值和,那么有

    \[f_{i,j}=f_{i-1,j-ki}+s_{i,z} \]

    \(s_{i,z}\)表示\(w=i\)的物品中前\(z\)大的价值和)

    这个式子很难用常规的优化,但是可以用四边形不等式。至于证明,我们有\(w_{i,j}=s_{j-i}\)
    要证明

    \[w_{i,j}+w_{i+1,j+1}\geq w_{i,j+1}+w_{i+1,j} \]

    \[s_{j-i}+s_{j-i}\geq s_{j-i+1}+s_{j-i-1} \]

    然后因为\(s_{i+1}-s_{i}\)是递减的,所以成立。

    那么我们现在对于每个枚举的\(w=i\),把所有的\(ik+j(\ j\in[0,i)\ )\)都分成一组。

    然后对于每一组我们都用四边形不等式优化,不过我忘了优化的方法了,还是记一下吧:

    对于所有的可能的决策我们用一个单调队列记录,顺带记录\(z_i\)表示队列里第\(i\)个决策和第\(i+1\)个决策的交叉点(在\(z_i\)之前\(q_{i}\)更优,\(z_i\)以之后\(q_{i+1}\)更优)。

    然后每次弹出队列前面的来找答案,加入的时候我们就二分出队尾和新加入的决策交换点,然后一直弹尾部直到不交叉。

    时间复杂度:\(O(mw\log m)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define ll long long
    using namespace std;
    const ll N=5e4+10;
    ll n,m,g,f[2][N],q[N],z[N];
    vector<ll> w[310];
    bool cmp(ll x,ll y)
    {return x>y;}
    ll calc(ll i,ll j,ll p,ll k)
    {return f[!g][i*p+k]+w[p][j-i-1];}
    ll bound(ll i,ll j,ll p,ll k){
    	ll l=i+1,r=(m-k)/p;
    	while(l<=r){
    		ll mid=(l+r)>>1;
    		if(calc(i,mid,p,k)<calc(j,mid,p,k))
    			l=mid+1;
    		else r=mid-1;
    	}
    	return l;
    }
    signed main()
    {
    	freopen("jewelry.in","r",stdin);
    	freopen("jewelry.out","w",stdout); 
    	scanf("%lld%lld",&n,&m);
    	for(ll i=1,c,v;i<=n;i++){
    		scanf("%lld%lld",&c,&v);
    		w[c].push_back(v);
    	}
    	g=0;
    	for(ll p=1;p<=300;p++){
    		if(w[p].empty())continue;g^=1;
    		memcpy(f[g],f[!g],sizeof(f[g]));
    //		memset(f[g],0,sizeof(f[g]));
    		sort(w[p].begin(),w[p].end(),cmp);
    		while(w[p].size()<=m/p)w[p].push_back(0);
    		for(ll i=1;i<w[p].size();i++)w[p][i]+=w[p][i-1];
    		for(ll k=0;k<p;k++){
    			ll head=1,tail=0;
    			for(ll i=0;i*p+k<=m;i++){
    				while(head<tail&&z[head]<=i)head++;
    				if(head<=tail)f[g][i*p+k]=max(f[g][i*p+k],calc(q[head],i,p,k));
    				while(head<tail&&z[tail-1]>=bound(i,q[tail],p,k))tail--;
    				z[tail]=bound(i,q[tail],p,k);q[++tail]=i;
    			}
    		}
    	}
    	for(ll i=1;i<=m;i++)
    		printf("%lld ",f[g][i]);
    	return 0;
    }
    
  • 相关阅读:
    Oracle中有大量的sniped会话
    Error 1130: Host '127.0.0.1' is not allowed to connect to this MySQL server
    汉字转换为拼音以及缩写(javascript)
    高效率随机删除数据(不重复)
    vs2010 舒服背景 优雅字体 配置
    mvc中的ViewData用到webfrom中去
    jquery ajax return值 没有返回 的解决方法
    zShowBox (图片放大展示jquery版 兼容性好)
    动感效果的TAB选项卡 jquery 插件
    loading 加载提示······
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15719451.html
Copyright © 2020-2023  润新知