zoukankan      html  css  js  c++  java
  • Atcoder Regular Contest 096 D Sweet Alchemy(贪心+多重背包)

    洛谷题面传送门 & Atcoder 题面传送门

    由于再过 1h 就是 NOI 笔试了所以题解写得会略有点简略。

    考虑差分,记 \(b_i=c_i-c_{fa_i}\),那么根据题意有 \(b_i\le d,i=2,3,4,\cdots,n\),而 \(b_1\) 则没有任何约束条件。而如果我们令某个 \(b_i\)\(1\),其余 \(b_i\) 均不变,那对应到原 \(c\) 序列上的操作效果就是 \(i\) 子树内的 \(c_j\)\(1\),其余 \(c_j\) 不变,因此预处理出 \(i\) 子树大小 \(siz_i\) 以及 \(i\) 子树内所有 \(m_j\) 之和 \(w_i\),问题就转化为:有 \(n\) 类物品,第 \(i\) 类代价为 \(w_i\),价值为 \(siz_i\),第 \(2,3,4,\cdots,n\) 类物品有 \(d\) 个,第 \(1\) 类物品有无限个,求用不超过 \(X\) 的代价买到的物品的最大价值和。

    一眼多重背包,不过此题数据范围高达 \(10^9\),直接稳了(指稳 TLE+MLE)。不过考虑一个错误的贪心,将所有物品按性价比 \(\dfrac{w_i}{siz_i}\) 从小到大排序然后贪心地取,每次能多取就多取,不过这个贪心显然是假的,反例随便举。不过这个贪心给了我们一点启发:对于两个满足 \(\dfrac{w_i}{siz_i}<\dfrac{w_j}{siz_j}\) 的物品 \(i,j\),如果目前 \(i\) 还能够再多取 \(siz_j\) 个,并且 \(j\) 目前取的个数多余 \(siz_i\) 个,那么我们完全可以让 \(j\) 少取 \(siz_i\) 个,\(i\) 多取 \(siz_j\) 个,这样肯定比原方案更优,也就是说我们钦定每个物品前 \(n\) 个中取了多少个,钦定完之后肯定就优先取 \(\dfrac{w_i}{siz_i}\) 小的物品更优了。

    因此考虑将所有物品先拿 \(\min(n,d)\) 个出来多重背包,即记 \(f_i\) 表示最少需要多少的代价才能够获得 \(i\) 的收益,剩余部分贪心即可,多重背包可以通过二进制分组/单调队列优化,复杂度 \(\mathcal O(n^4\log n)/\mathcal O(n^4)\),本人使用的二进制分组。

    const int MAXN=50;
    const int MAXV=1.25e5;
    const int MAXI=350;
    int n,m,d,siz[MAXN+5],ord[MAXN+5];ll w[MAXN+5];
    int hd[MAXN+5],to[MAXN+5],nxt[MAXN+5],ec=0;
    void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
    void dfs(int x){
    	siz[x]=1;
    	for(int e=hd[x];e;e=nxt[e]){
    		int y=to[e];dfs(y);
    		siz[x]+=siz[y];w[x]+=w[y];
    	}
    }
    bool cmp(int x,int y){return 1ll*w[x]*siz[y]<1ll*w[y]*siz[x];}
    ll dp[MAXV+5],W[MAXI+5];
    int V[MAXI+5],item_n=0,lim[MAXN+5];
    int main(){
    	scanf("%d%d%d%lld",&n,&m,&d,&w[1]);
    	for(int i=2,f;i<=n;i++) scanf("%lld%d",&w[i],&f),adde(f,i);
    	dfs(1);for(int i=1;i<=n;i++) ord[i]=i;sort(ord+1,ord+n+1,cmp);
    	int mx=0;memset(dp,63,sizeof(dp));dp[0]=0;
    	for(int i=1;i<=n;i++){
    		int up=min((ord[i]==1)?0x3f3f3f3f:d,n);
    		mx+=up*siz[ord[i]];
    		int k=31-__builtin_clz(up);
    		for(int j=0;j<k;j++){
    			W[++item_n]=w[ord[i]]<<j;
    			V[item_n]=siz[ord[i]]<<j;
    		} W[++item_n]=w[ord[i]]*(up-(1<<k)+1);
    		V[item_n]=siz[ord[i]]*(up-(1<<k)+1);
    		lim[ord[i]]=((ord[i]==1)?0x3f3f3f3f:d)-up;
    //		printf("%d %d\n",ord[i],lim[ord[i]]);
    	} int ans=0;
    	for(int i=1;i<=item_n;i++) for(int j=mx;j>=V[i];j--)
    		chkmin(dp[j],dp[j-V[i]]+W[i]);
    	for(int i=0;i<=mx;i++) if(dp[i]<=m){
    		int lft=m-dp[i],sum=i;//printf("%d %d %d\n",i,lft,sum);
    		for(int j=1;j<=n;j++){
    			int can=min((int)(lft/w[ord[j]]),lim[ord[j]]);
    //			printf("%d\n",can);
    			sum+=can*siz[ord[j]];lft-=can*w[ord[j]];
    		} chkmax(ans,sum);
    	} printf("%d\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    版本控制工具 svn 一
    测试用例 (一)
    Jmeter (四) 关联
    Jmeter (三) 集合点 、检查点 (断言)
    Jmeter (二) 参数化
    Jmeter (一) 安装
    自动化测试 selenium 测试软件安装
    postman 接口测试(一)
    PHP使用Redis的Pub/Sub(发布订阅)命令
    idea控制台乱码问题
  • 原文地址:https://www.cnblogs.com/ET2006/p/arc096D.html
Copyright © 2011-2022 走看看