首页 > 其他分享 >DP优化——树上依赖性背包&P6326 Shopping

DP优化——树上依赖性背包&P6326 Shopping

时间:2024-12-28 14:19:34浏览次数:5  
标签:P6326 连通 背包 Shopping int 复杂度 要选 dfn DP

P6326 Shopping

题意等价于要买一个连通块。

首先如果我们能求出一个 dp 数组:
\(f_{i,j}\) 表示 \(i\) 子树内,有 \(j\) 元,一定要选 \(i\),能得到的最大价值。
那么 \(f_{1,m}\) 就是一定选根的答案。
然后点分治即可。

接下来就是怎么在合理的复杂度内求出 dp 数组。
直接背包显然不行,因为这不是一个普通的树形背包,他的第二维跟子树大小无关,所以合并子树的复杂度是单次 \(O(m^2)\),总复杂度是 \(O(nm^2)\)。

然后就诞生了一种很厉害的科技——树上依赖性背包
因为选儿子就一定要选父亲,所以管他叫这个名字。
具体做法是:
设 \(f_{i,j}\) 表示考虑 dfn 位于 \([i,n]\) 中的点,有 \(j\) 元钱能得到的最大价值。
下面设根为 \(1\)。
因为根是 dfn 最小的点,它一定要选,特判一下。
接下来考虑 \(dfn=2\) 的点,不妨设为 \(2\),他一定是 \(1\) 的儿子,他有两种选择:

  1. 不选,那他子树内的点都不能选,而众所周知,子树内的点的 dfn 是连续的一段区间,那么转移就是:\(f_{2 + Size_2,j} \to f_{2,j}\)。
    此时剩下的问题相当是把 \(2\) 这棵子树去掉了,仍然是一棵树,并且 dfn 也是一个后缀。
    并且如果在剩下的这棵树内选择了一个连通块,那么放到原树内选出的也是一个连通块。
    所以他是一个子问题。
  2. 选,转移是:\(max_{1\le k \le d_2}(f_{3,j-c_2\times k} + w_2\times k) \to f_{2,j}\)。
    这个转移本质是多重背包,可以用二进制拆分优化到 \(O(\log d)\),也可以用单调队列优化到均摊 \(O(1)\)。
    此时 \(1,2\) 构成了一个连通块,我们把它视为新的根,原本 \(1,2\) 的子树接到这个新的根上。
    那么构成的仍然是一棵树,dfn 是一个后缀,且如果在这棵新树里选出了一个连通块,那么放到原树上也是一个连通块。
    所以它也是一个子问题。
    然后把新的根当根继续考虑 \(dfn=3\) 的点。

答案是 \(f_{1,m}\)。

这个 dp 是 \(O(nm\log d)\) 或者 \(O(nm)\) 的。
总复杂度乘一个点分治的 \(O(\log n)\) 即可。

#include<bits/stdc++.h>
using namespace std;
const int N=500+5,M=4000+5,inf=0x3f3f3f3f3f3f3f3f;
inline int read(){
    int w = 1, s = 0;
    char c = getchar();
    for (; c < '0' || c > '9'; w *= (c == '-') ? -1 : 1, c = getchar());
    for (; c >= '0' && c <= '9'; s = 10 * s + (c - '0'), c = getchar());
    return s * w;
}
int n,m,ans,w[N],c[N],d[N];
int tot,head[N],to[N<<1],Next[N<<1];
void add(int u,int v){
	to[++tot]=v,Next[tot]=head[u],head[u]=tot;
} 

int rt,sum,Size[N],maxn[N],rev[N],num,f[N][M],g[M];
bool vis[N];
void Get_zx(int u,int fa){
	Size[u]=1;
	maxn[u]=0;
	for(int i=head[u];i;i=Next[i]){
		int v=to[i];
		if(vis[v]||v==fa) continue;
		Get_zx(v,u);
		Size[u]+=Size[v];
		maxn[u]=max(maxn[u],Size[v]);
	}
	maxn[u]=max(maxn[u],sum-Size[u]);
	if(maxn[u]<maxn[rt]) rt=u;
}
void dfs(int u,int fa){
	rev[++num]=u;
	Size[u]=1;
	for(int i=head[u];i;i=Next[i]){
		int v=to[i];
		if(vis[v]||v==fa) continue;
		dfs(v,u);
		Size[u]+=Size[v];
	}
}

void calc(){
	for(int i=1;i<=num+1;i++) for(int j=0;j<=m;j++) f[i][j]=(i==num+1)?0:-inf;
	for(int i=num;i>=1;i--){
		int u=rev[i],lst=d[u]-1;  //因为一定要选一个,所以在一开始就减掉 
		vector<int> v;
		for(int mi=1;lst>=mi;mi<<=1){
			v.emplace_back(mi);
			lst-=mi;
		} 
		if(lst) v.emplace_back(lst);
		for(int j=0;j<=m;j++) g[j]=(j>=c[u])?(f[i+1][j-c[u]]+w[u]):-inf;  //确保至少选一个 
		for(int x:v) for(int j=m;j>=x*c[u];j--) g[j]=max(g[j],g[j-x*c[u]]+x*w[u]);
		for(int j=0;j<=m;j++){
			f[i][j]=g[j];  //起码要选一个 
			if(i!=1) f[i][j]=max(f[i][j],f[i+Size[u]][j]);			
		}
	}
	ans=max(ans,f[1][m]);
}
void solve(int t){
	vis[t]=true;
	calc();
	for(int i=head[t];i;i=Next[i]){
		int v=to[i];
		if(vis[v]) continue;
		rt=num=0;
		sum=maxn[rt]=Size[v];
		Get_zx(v,t);
		dfs(rt,0);  
		solve(rt);
	}
}

void Init(){
	tot=ans=0;
	for(int i=1;i<=n;i++) head[i]=vis[i]=0;
}
signed main(){
	int T=read();
	while(T--){
		n=read(),m=read();
		Init();
		
		for(int i=1;i<=n;i++) w[i]=read();	
		for(int i=1;i<=n;i++) c[i]=read();	
		for(int i=1;i<=n;i++) d[i]=read();	
		for(int i=1;i<n;i++){
			int u=read(),v=read();
			add(u,v),add(v,u);
		}
		
		rt=num=0;
		sum=maxn[rt]=n;
		Get_zx(1,0);
		dfs(rt,0);  
		solve(rt); 
		
		printf("%lld\n",ans);
	}
	return 0;
}

标签:P6326,连通,背包,Shopping,int,复杂度,要选,dfn,DP
From: https://www.cnblogs.com/FloatingLife/p/18637461

相关文章

  • E92 换根DP+倍增 P5666 [CSP-S2019] 树的重心
    视频链接:E92换根DP+倍增P5666[CSP-S2019]树的重心_哔哩哔哩_bilibili   P5666[CSP-S2019]树的重心-洛谷|计算机科学教育新生态(luogu.com.cn)//换根DP+倍增O(nlogn)#include<iostream>#include<cstring>#include<algorithm>#include<vector>us......
  • 9.4-14域横向-CobaltStrike&SDN&RDP
    域横向RDP-mimikatz1、RDP明文密码链接2、RDP密文hash链接域横向SPN服务-探针,请求,导出,破解,重写SPN扫描当计算机加入域时,主SPN会自动添加到域的计算机账号的ServicePrincipalName属性中。在安装新的服务后,SPN也会被记录在计算机账号的相应属性中。SPN扫描也称为“扫描Kerber......
  • TCP-UDP调试工具推荐:Socket通信测试教程(附详细图解)
    前言在网络编程与应用开发中,调试始终是一项不可忽视的重要环节。尤其是在涉及TCP/IP、UDP等底层网络通信协议时,如何确保数据能够准确无误地在不同节点间传输,是许多开发者关注的核心问题。调试的难点不仅在于定位连接建立、数据流控制及错误处理等问题,还在于快速、高效地解决这些......
  • DP1363F是一款高度集成的非接触读写芯片,高性能、多协议NFC读卡IC
    DP1363F是一款高度集成的非接触读写芯片,集强大的多协议支持、最高射频输出功率,以及突破性技术低功耗卡片检测等优势于一身,满足市场对更高集成度、更小外壳和互操作性的需求,适用于银行、电子政务、交通、移动支付等众多基础设施应用。DP1363F支持下列操作模式:•读写模式支持ISO/......
  • xzy的树形dp题单
    P3647[APIO2014]连珠线题意简述:树上加点游戏,分红蓝边,有边权,点编号为\(1\)到\(n\)。游戏从任意一个点开始,每次操作添加一个新点\(w\)。Append(w,v):连红边\((w,v)\)。Insert(w,u,v):删掉红边\((u,v)\),连蓝边\((u,w)\)和\((w,v)\)。给定游戏结局的树,最大化最终蓝边......
  • 玻璃Dpgf参数及其对光学系统的影响
    前言与目录在光学玻璃选择过程中,使用Model选项时,需要输入nd、Vd和Dpgf这三个参数。本文将探讨Dpgf的具体作用及其对整个光学系统的影响。目录1、Dpgf的作用及其影响:2、Model选项的使用:3、获取塑料的dpgf值的方法:1、Dpgf的作用及其影响:Dpgf,即局部色散或相对部分色散......
  • 使用UDP探测steam游戏延迟
    需求: 众所周知网络传输上对icmp,tcp,udp数据包是有区别对待的,当我们使用icmp去探测游戏服务器时毫无问题,但是游戏延迟异常或频繁掉线 此时不妨尝试下UDP探测,来求证是否运营商对UDP数据做了限制基础理论 icmp探测 icmp探测的前提是服务端会对icmp报文进行回包 ......
  • Java面试要点97 - Java中ThreadPoolExecutor源码解析
    文章目录引言一、核心属性1.1状态与线程数量的原子控制1.2任务队列与工作线程组二、Worker线程包装类2.1Worker类的设计三、任务提交源码分析3.1execute方法实现3.2addWorker核心方法四、任务执行源码分析4.1runWorker方法实现4.2getTask方法分析五、线程池......
  • 探索鸿蒙的蓝牙A2DP与访问API:从学习到实现的开发之旅
    完成了鸿蒙系统中一系列的学习与实际应用开发后,我的开发旅程逐渐走向了更复杂的领域。这次,我决定挑战蓝牙相关功能。蓝牙技术是现代设备互联的核心之一,而鸿蒙系统提供的BluetoothA2DPAPI和BluetoothAccessAPI为开发者带来了便捷的接口。不管三七二十一了,咱们直接上API13版本,然......
  • wordpress固定链接设置
     通过上面的测试发现,切换不同链接结构,同一张网页但是网址却发生了改变。为什么要改变网址的结构?主要原因,是为了搜索引擎优化。搜索引擎不喜欢带有问号的网址,也不喜欢层级特别深的网址。所以,WordPress允许你去设置网址的结构,让网址更符合搜索引擎的要求(官方说法是更美观)。%po......