首页 > 其他分享 >浅谈缺一分治

浅谈缺一分治

时间:2023-08-26 10:23:05浏览次数:40  
标签:浅谈 int 分治 缺一 mid re col

这个思想好像并不常见,我也是一个月前在 lxr 讲 dp 的课上第一次听说这个名字,后来 ACM 比赛中出了一个 gym 题,也可以运用相同的思想,但是脑子短路没搞出来,现在随便记一记。

缺一分治,顾名思义,就是在题目中有一个位置的限制和其它位置不一样,我们利用分治的思想,枚举每一个位置,然后把 \(O(n)\) 的部分优化到 \(O(\log n)\)。

给个典题,也是 lxr 讲的:CF1442D sum

给定 \(n\) 个不降的数组。有一个值 \(ans\),初始为 \(0\)。你需要进行如下操作 \(k\) 次:选择一个数组,把 \(ans\) 加上数组的第一个元素,之后把它删除。

请求出 \(ans\) 最大是多少,所有数组的元素总个数 \(\leq 10^6\),\(n,k\leq 3000\)。

首先这题有一个很重要的性质,没有这个性质就谈不上运用缺一分治:

至多有一个序列没被选满。

这个证明用微调法证一下即可,具体可以看这篇题解

然后就可以用上我们的缺一分治了,我们先求出每个序列的总和,设 \(f(l,r)\) 表示选择一部分的那个序列在 \([l,r]\) 区间内,选出的最大值,然后我们进行分治。

我们先把 \([l,mid]\) 序列做一个背包 DP,然后递归 \([mid+1,r]\);然后撤销贡献,把 \([mid+1,r]\) 序列做一个背包 DP,然后递归 \([l,mid]\)。就这样,一直到 \(l = r\) 时,此时的这个序列就是我们的“一”,也就是没选满的这个,我们可以枚举它选了几个,然后更新我们的答案。显然,最后 \(f(1,n)\) 便是我们要求的答案。

需要注意的我们撤销贡献的方法是在递归的时候多加一维度 \(col\),表示当前分治到了第几层,然后我们撤销贡献的时候就把它赋值成上一层的值即可。

这样本来是 \(O(nk^2)\) 的算法,就被我们优化到 \(O(nk\log n)\) 了。

核心代码:

il void dfs(int l,int r,int col)//col表示当前是第几层
{
	if(l == r)
	{
	//考虑部分取的数组在 [l,r] 中的最优解。
	//如果 l<r,先把 [mid+1,r] 中的元素加入背包,递归计算 [l,mid],再把背包复原,把 [l,mid] 中的元素加入背包,递归计算 [mid+1,r] 。
	//这样一个nk^2的算法,我们只需要logk层,就可以 nklogn的复杂度内求出来了
		for(re int i=min(k,cnt[l]);i>=0;i--)
			ans = max(ans,f[k-i][col] + a[l][i]);
		return ;
	}
	int mid = (l+r) >> 1;
	for(re int i=0;i<=k;i++) f[i][col+1] = f[i][col];
	for(re int i=mid+1;i<=r;i++)
		for(re int j=k-cnt[i];j>=0;j--) f[j+cnt[i]][col+1] = max(f[j+cnt[i]][col+1],f[j][col+1]+sum[i]);
	dfs(l,mid,col+1);
	for(re int i=0;i<=k;i++) f[i][col+1] = f[i][col];
	for(re int i=l;i<=mid;i++)
		for(re int j=k-cnt[i];j>=0;j--) f[j+cnt[i]][col+1] = max(f[j+cnt[i]][col+1],f[j][col+1]+sum[i]);
	dfs(mid+1,r,col+1);
}

signed main()
{
	n = read() , k = read();
	for(re int i=1;i<=n;i++)
	{
		cnt[i] = read() , a[i].push_back(0);
		for(re int j=1;j<=cnt[i];j++)
		{
			x = read() , sum[i] += x;
			a[i].push_back(sum[i]);
		}
	}
	dfs(1,n,0);
	cout << ans;
	return 0;
}

总之,缺一分治就是把那个特殊的留在最后,同时处理出不特殊的部分的贡献,然后分治往下找的过程。

还有一个 ACM 出上的 gym 题:Gym-104090C No Bug No Game

这个题你同样可以知道,只有一个序列不会取最后一个数,而其他的都是取最后一个数的,然后你就可以对这个不取最后一个数的位置进行缺一分治,然后这样做大约也是 \(O(nk\log n)\) 的,可以比较极限的通过,正解是 \(O(nkp)\) 的。

核心代码:

il void dfs(int l,int r,int col)
{
	if(l == r)
	{
		ans = max(ans,f[k][col]);
		for(re int i=1;i<=min(k,w[l]);i++)
			ans = max(ans,f[k-i][col] + v[l][i]);
		return ;
	}
	int mid = (l+r) >> 1;
	for(re int i=0;i<=k;i++) f[i][col+1] = f[i][col];
	for(re int i=mid+1;i<=r;i++)
		for(re int j=k;j>=w[i];j--) f[j][col+1] = max(f[j][col+1],f[j-w[i]][col+1]+v[i][w[i]]);
	dfs(l,mid,col+1);
	for(re int i=0;i<=k;i++) f[i][col+1] = f[i][col];
	for(re int i=l;i<=mid;i++)
		for(re int j=k;j>=w[i];j--) f[j][col+1] = max(f[j][col+1],f[j-w[i]][col+1]+v[i][w[i]]);
	dfs(mid+1,r,col+1);
}

signed main()
{
	n = read() , k = read();
	int Max = 0;
	for(re int i=1;i<=n;i++)
	{
		w[i] = read(); 
		sump += w[i];
		for(re int j=1;j<=w[i];j++)
			v[i][j] = read() , Max = max(Max,v[i][j]);
		sumw += v[i][w[i]];
	}
	memset(f , -0x3f , sizeof f);
	f[0][0] = 0;
	dfs(1,n,0);
	if(sump <= k) ans = max(ans,sumw);
	cout << ans;
	return 0;
}

这就是缺一分治的基本思想,目前我就只遇到了这两个题,如果有什么新花样还会补充。

标签:浅谈,int,分治,缺一,mid,re,col
From: https://www.cnblogs.com/bloodstalk/p/17658428.html

相关文章

  • 浅谈谷歌优化之“AMP 状态”报告
    报告内容严重问题:受严重AMP问题影响的网页无法在Google上显示。在您的网站上发现的严重问题列表会显示在AMP报告顶级页面中图表的正下方,标题为AMP网页无效的原因。点击该列表中的某个问题可查看包含所选问题的网页。非严重问题(警告):存在非严重问题的AMP网页只要不是同时存在任......
  • 浅谈视频汇聚平台EasyCVR视频平台在城市安全综合监测预警台风天气中的重要作用
    夏日已至,台风和暴雨等极端天气频繁出现。在城市运行过程中,台风所带来的暴雨可能会导致城市内涝等次生灾害,引发交通瘫痪、地铁停运、管网泄漏爆管、路面塌陷、防洪排涝、燃气爆炸、供热安全、管廊安全、消防火灾等安全隐患,影响城市的正常运行,甚至造成人员伤亡。面对台风带来的城市灾......
  • go.mod 浅谈理解
    go.mod对于上次接触golang这门语言还是在上次了,最近对zig比较感兴趣,而突然折腾回golang的时候发现这玩意在1.1.1版本更新了一个叫go.mod的东西。go.mod是Go语言的官方包管理工具,用于解决之前没有地方记录依赖包具体版本的问题,方便依赖包的管理。Go.mod其实就是一个Module......
  • 浅谈LIS
    连续上升子序列(LIS)定义\(一个序列中单调不减的子序列或单调递增的子序列(看题决定,做法几乎一致),下文以严格上升子序列为例\)做法一\(暴力dp,设f_i表示以i结尾的LIS的最长长度,f_i=max(f_j|j<i,a_j<a_i)+1。\)\(dp比较好理解,就是由上一个比当前小的数加上当前的数组成最长上升......
  • 点分治板题
    点分治点分治的思想:每次选定一个点,然后分别在它的子树内求解①,再计算跨越子树的情况,考虑当前点和每棵子树内的点②以及两两子树内的③。(有点绕)如图,红代表①,紫代表②,绿代表③。这样求解的复杂度取决去递归的层数,若树是一条链,那么若每次选择的是链的一个端点,那么一共会有\(n\)......
  • 浅谈 Linux 下 vim 的使用
    Vim是从vi发展出来的一个文本编辑器,其代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。Vi是老式的字处理器,功能虽然已经很齐全了,但还有可以进步的地方。Vim可以说是程序开发者的一项很好用的工具。对于大多数用户来说,Vim刚开始学习的时候可能会进......
  • 浅谈视频汇聚平台EasyCVR中AI中台的应用功能
    AI中台是将人工智能技术如深度学习、计算机视觉、知识图谱、自然语言理解等模块化,集约硬件的计算能力、算法的训练能力、模型的部署能力、基础业务的展现能力等人工智能能力,结合中台的数据资源,封装成整体中台系统。在EasyCVR视频共享融合云平台中,AI中台是专门提供人工智能视频......
  • 浅谈谷歌“网页体验”报告
    “网页体验”报告提供了您网站访问者的用户体验摘要。Google会评估您网站上各个网址的网页体验指标,并会将这些指标作为网址在Google搜索结果中的排名衡量因素。关于网页体验谷歌会针对每个网址评估网页体验。评估结果和报告旨在帮助网站创建能够为访问者提供更好用户体验的网页......
  • 浅谈谷歌优化之“智能出价模式”
      智能点击付费可以帮助您通过人工出价获得更多转化。其工作原理是,根据点击在您网站上促成销售或其他转化的可能性大小,相应地自动调整您的人工出价。“目标每次转化费用”和“目标广告支出回报率”这两项智能出价策略会分别根据您设定的每次转化费用目标值和广告支出回报率目标......
  • StoneDB首席架构师李浩受邀采访:浅谈KPI与开源的可持续发展,认可长期主义很重要
    编者荐语:<br>StoneDB开源社区PMC、首席架构师李浩老师近期接受了ITPUB的采访,本文是对众多采访嘉宾的汇编,李浩老师对KPI与开源提出了独特见解,推荐大家阅读。<br>以下文章来源于ITPUB,作者李雪薇近年来,全球开源生态迈向高速发展的崭新阶段,很多企业、社区和个人都将关注点......