2016NOIP普及组真题 4. 魔法阵

线上OJ:

一本通:http://ybt.ssoier.cn:8088/problem_show.php?pid=1976

本题作为第四题,想拿满分有难度。但是暴力拿些分还是做得到的。
满分需要用 前缀和 来化简for循环。

核心语句:

$ x_a < x_b < x_c < x_d $ ①
$ x_b - x_a = 2(x_d - x_c) $ ②
$ x_b - x_a < (x_c - x_b)/3 $ ③

核心思想:

由于魔法值 n 不超过 15000,但魔法球的数量 m 可达到40000。
所以选择反向枚举答案:即对所有的魔法值进行枚举(先找出 n 范围内所有符合上述三个公式的魔法组合数字),这样可避免对魔法球进行排序。

另外,由于魔法球的魔法数值可能相同,所以在计算每一个数字的出现次数时,要考虑其他数字的 组合 出现情况。如下图所示:

在这里插入图片描述

上图中,1# 魔法球在物品A中出现的次数为6次,分别为:
1#,2#,3#,4#
1#,2#,7#,4#
1#,2#,9#,4#
1#,6#,3#,4#
1#,6#,7#,4#
1#,6#,9#,4#

上图中,2# 魔法球在物品B中出现的次数为9次,分别为:
1#,2#,3#,4#
1#,2#,7#,4#
1#,2#,9#,4#
5#,2#,3#,4#
5#,2#,7#,4#
5#,2#,9#,4#
8#,2#,3#,4#
8#,2#,7#,4#
8#,2#,9#,4#

所以每一个魔法球在某个位置出现的 次数 = 剩余位置魔法球数量的乘积(组合思想)

题解代码:
解法一、
#include <bits/stdc++.h>
#define MAXN 40005
using namespace std;struct Node
{int val, id;	// 存储节点的值和 id。 id用于最终的输出
};
Node node[MAXN];int n, m;	// m 个魔法物; 魔法值不超过 n
int ge[MAXN]; // 记录val为i的魔法物的个数。用于计算组合
int cnta[MAXN], cntb[MAXN], cntc[MAXN], cntd[MAXN];		// cnta[i]表示val为i的魔法物在A位置出现的次数
// cntb[i]表示val为i的魔法物在B位置出现的次数, cntc[i]表示val为i的魔法物在C位置出现的次数 等// 暴力枚举 70% 分数。
int main()
{scanf("%d%d",&n, &m);int x;for(int i = 1; i <= m; i++){scanf("%d", &x);node[i].val = x;node[i].id = i;ge[x]++;  // 统计魔法值为x的物品个数}// 反向枚举结果:对所有的魔法值进行枚举(即先找出 n 范围内所有符合要求的魔法组合数字),可避免排序。// 不是对每个魔法球的魔法值进行枚举(每一轮都是枚举40000和枚举15000的区别)for(int ai = 1; ai <= n - 3; ai++)for(int bi = ai + 1; bi <= n - 2; bi++)for(int ci = bi + 1; ci <= n - 1; ci++)for(int di = ci + 1; di <= n; di++)if( ( (bi-ai) == 2*(di-ci) ) && ( 3*(bi-ai) < (ci-bi) ) ){	// 如果找到一组符合要求的魔法值,则更新各数字在对应位置出现的次数// 考虑到不同的魔法球会有相同的魔法值,所以在作组合计算时对其余位置作数量的乘法cnta[ai] += ge[bi] * ge[ci] * ge[di];cntb[bi] += ge[ai] * ge[ci] * ge[di];cntc[ci] += ge[ai] * ge[bi] * ge[di];cntd[di] += ge[ai] * ge[bi] * ge[ci];break;	// 1个abc只能对应1个d,如果找到了,直接退出循环}for(int i = 1; i <= m; i++){int t = node[i].val;printf("%d %d %d %d\n",cnta[t], cntb[t], cntc[t], cntd[t]);}return 0;
}

以上暴力代码比较简单,只要注意 反向枚举,就可以在考场上快速拿到70%的分数。

解法二、优化区间

接下来对解法一进行优化。
把对公式③ $ x_b - x_a < (x_c - x_b)/3 $ 的利用,从条件判断改为直接赋值给ci的范围。
因为 x b − x a < ( x c − x b ) / 3 x_b-x_a < (x_c-x_b)/3 xbxa<(xcxb)/3,移项后可得 x c > 4 x b − 3 x a x_c > 4x_b-3x_a xc>4xb3xa, 所以 x c x_c xc 4 x b − 3 x a + 1 4x_b-3x_a+1 4xb3xa+1开始取值。
所以 c i = 4 ∗ b i − 3 ∗ a i + 1 ci = 4 * bi - 3 * ai + 1 ci=4bi3ai+1
同时,由于1个abc只能对应1个d,如果找到了符合条件的d,就退出最内层的循环。

#include <bits/stdc++.h>
#define MAXN 40005
using namespace std;struct Node
{int val, id;	// 存储节点的值和 id。 id用于最终的输出
};
Node node[MAXN];int n, m;	// m 个魔法物; 魔法值不超过 n
int ge[MAXN]; // 记录val为i的魔法物的个数。用于计算组合
int cnta[MAXN], cntb[MAXN], cntc[MAXN], cntd[MAXN];		// cnta[i]表示val为i的魔法物在A位置出现的次数
// cntb[i]表示val为i的魔法物在B位置出现的次数, cntc[i]表示val为i的魔法物在C位置出现的次数 等// 暴力枚举 80% 分数。
int main()
{scanf("%d%d",&n, &m);int x;for(int i = 1; i <= m; i++){scanf("%d", &x);node[i].val = x;node[i].id = i;ge[x]++;  // 统计魔法值为x的物品个数}// 反向枚举结果:对所有的魔法值进行枚举(即先找出 n 范围内所有符合要求的魔法组合数字),可避免排序。// 不是对每个魔法球的魔法值进行枚举(每一轮都是枚举40000和枚举15000的区别)for(int ai = 1; ai <= n - 3; ai++)for(int bi = ai + 1; bi <= n - 2; bi++)for(int ci = 4 * bi - 3 * ai + 1; ci <= n - 1; ci++)for(int di = ci + 1; di <= n; di++)if( (bi-ai) == 2*(di-ci) ){   // 如果找到一组符合要求的魔法值,则更新各数字在对应位置出现的次数// 考虑到不同的魔法球会有相同的魔法值,所以在作组合计算时对其余位置作数量的乘法cnta[ai] += ge[bi] * ge[ci] * ge[di];cntb[bi] += ge[ai] * ge[ci] * ge[di];cntc[ci] += ge[ai] * ge[bi] * ge[di];cntd[di] += ge[ai] * ge[bi] * ge[ci];break;	// 1个abc只能对应1个d,如果找到了,直接退出循环}for(int i = 1; i <= m; i++){int t = node[i].val;printf("%d %d %d %d\n",cnta[t], cntb[t], cntc[t], cntd[t]);}return 0;
}

以上代码优化后可以拿到80%分数。
继续优化。

解法三、四层循环优化为三层

在这里插入图片描述

公式 ①②③ 告诉了我们几组关系,
1、如果CD之间的距离为一个步长,则AB之间的距离必为两倍步长,且BC之间的距离要大于六倍步长
2、假设步长为 k, 且已知 ai,则
● bi 可直接推断出为 = ai+2k
● ci 的左边界可以推断出 = ai + 8k + 1,从左边界向右枚举即可
● di 可直接推断出为 = ci+k = ai + 9k + 1
如此一来,之前的四层for循环就变成了三层。只需要枚举步长k,ai和ci即可。代码见下

#include <bits/stdc++.h>
#define MAXN 40005
using namespace std;struct Node
{int val, id;	// 存储节点的值和 id。 id用于最终的输出
};
Node node[MAXN];int n, m;	// m 个魔法物; 魔法值不超过 n
int ge[MAXN]; // 记录val为i的魔法物的个数。用于计算组合
int cnta[MAXN], cntb[MAXN], cntc[MAXN], cntd[MAXN];		// cnta[i]表示val为i的魔法物在A位置出现的次数
// cntb[i]表示val为i的魔法物在B位置出现的次数, cntc[i]表示val为i的魔法物在C位置出现的次数 等// 暴力枚举 90% 分数
int main()
{scanf("%d%d",&n, &m);int x;for(int i = 1; i <= m; i++){scanf("%d", &x);node[i].val = x;node[i].id = i;ge[x]++;  // 统计魔法值为x的物品个数}for(int k = 1; 9 * k < n; k++ )for(int ai = 1; ai <= n - 9*k; ai++)	// ai步长为2,可保证bi-ai为2的整数倍{int bi = ai + 2*k;for(int ci = ai + 8*k + 1; ci <= n - k; ci++)	// 根据公式3,枚举ci{				int di = ci + k;cnta[ai] += ge[bi] * ge[ci] * ge[di];cntb[bi] += ge[ai] * ge[ci] * ge[di];cntc[ci] += ge[ai] * ge[bi] * ge[di];cntd[di] += ge[ai] * ge[bi] * ge[ci];				}	}						for(int i = 1; i <= m; i++){int t = node[i].val;printf("%d %d %d %d\n",cnta[t], cntb[t], cntc[t], cntd[t]);}return 0;
}

以上代码优化后可以拿到90%分数。依然没达到满分。
继续优化。

解法四、三层循环优化为两层(前缀和+后缀和)

三层for循环依然会超时,故如果满分需要压缩到2层for循环。我们看下面这张图,会发现:

1、从右往左看,当 CD固定 时,AB可以移动,移动的 右边界 是BC距离为6k+1。
即:右边界左侧每一对 间隔 步长2kAB 都符合题意。所以对AB点的计算可以采用 前缀和

在这里插入图片描述

2、同理(图二),从左往右 看,当 AB固定时 ,CD可以移动,移动的 左边界 是BC距离为6k+1。
即:左边界右侧 的每一对间隔 步长kCD 都符合题意。所以对CD点的计算可以采用后缀和(因为从后往前计算)。

在这里插入图片描述

详细代码见下:

#include <bits/stdc++.h>
#define MAXN 40005
using namespace std;struct Node
{int val, id;	// 存储节点的值和 id。 id用于最终的输出
};
Node node[MAXN];int n, m;	// m 个魔法物; 魔法值不超过 n
int ge[MAXN]; // 记录val为i的魔法物的个数。用于计算组合
int cnta[MAXN], cntb[MAXN], cntc[MAXN], cntd[MAXN];		// cnta[i]表示val为i的魔法物在A位置出现的次数
// cntb[i]表示val为i的魔法物在B位置出现的次数, cntc[i]表示val为i的魔法物在C位置出现的次数 等// 暴力枚举 90% 分数。
// 枚举ai,bi,ci。根据公式2,如果bi-ai不满足是2的倍数,则不存在ci和di,故直枚举下一个bi。
// 由于1个ai、bi、ci只能对应1个di。所以在枚举ci时,对di进行直接计算即可。
int main()
{scanf("%d%d",&n, &m);int x;for(int i = 1; i <= m; i++){scanf("%d", &x);node[i].val = x;node[i].id = i;ge[x]++;  // 统计魔法值为x的物品个数}// 3层for循环依然会超时,故需要压缩到2层for循环// 由于当 di 固定时,ci 就固定。当 ci 固定时,bi能移动的右边界就固定,ai的右边界也就固定。所以,只要不超过右边界的每一对bi和ai都满足已知固定di和ci的魔法阵。// 所以,当di和ci固定时,只有bi和ai是变量,此时对每一对bi和ai求前缀和。同理,当ai 和 bi 固定时,对每一对ci和di求后缀和。for(int k = 1; 9 * k < n; k++ ){int sumab = 0;// 因为对ai和bi求前缀和,所以di枚举的顺序是从小到大。所以di从9*k + 2开始(9*k+1+A的起始是1)for(int di = 9*k + 2; di <= n; di++){int ci = di - k;	// 当 di 固定时,ci 也固定int bi = di - 7*k - 1; // 当ci固定时,bi的右边界可固定int ai = di - 9*k - 1; // 当bi的右边界固定时,ai的右边界也可固定sumab += ge[ai] * ge[bi];	// 由于di是从最小值枚举,所以ai和bi可以用前缀和记录每一组有效状态cntc[ci] += sumab * ge[di];cntd[di] += sumab * ge[ci];}int sumcd = 0;for(int ai = n - 9*k - 1; ai >= 1; ai--){int bi = ai + 2*k;  // 当 ai 固定时,bi 也固定int ci = ai + 8*k + 1; // 当bi固定时,ci的左边界可固定int di = ai + 9*k + 1; // 当ci的左边界固定时,di的左边届也固定sumcd += ge[ci] * ge[di];  // 由于ai是从大到小枚举,所以ci和di可以用后缀和cnta[ai] += ge[bi] * sumcd; cntb[bi] += ge[ai] * sumcd;}      }				for(int i = 1; i <= m; i++){int t = node[i].val;printf("%d %d %d %d\n",cnta[t], cntb[t], cntc[t], cntd[t]);}return 0;
}

以上第四种方法详细讲解可参见 https://www.luogu.com.cn/article/4lsb9owi

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/819213.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue webpack打包配置生成的源映射文件不包含源代码内容、加密混淆压缩

前言&#xff1a;此案例使用的是vue-cli5 一、webpack源码泄露造成的安全问题 我们在打包后部署到服务器上时&#xff0c;能直接在webpack文件下看到我们项目源码&#xff0c;代码检测出来是不安全的。如下两种配置解决方案&#xff1a; 1、直接在项目的vue.config.js文件中加…

Android 使用ping命令判断当前网络状态

一. 介绍 ping命令是用来测试和诊断网络连接问题的基本命令&#xff0c;当然我们的终端设备&#xff08;手机/平板/车机&#xff09;都可以用这个命令来判断当前网络是否有流量的状态&#xff0c;本篇文章主要介绍Linux的ping命令&#xff0c;因为Android系统也是使用了Linux内…

OpenAI官宣位于东京的首个亚洲办公室,并将发布专为日语优化的GPT-4定制模型!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

科技云报道:AI大模型疯长,存储扛住了吗?

科技云报道原创。 AI大模型正在倒逼数字基础设施产业加速升级。 过去一年半&#xff0c;AI大模型标志性的应用相继出现&#xff0c;从ChatGPT到Sora一次次刷新人们的认知。震撼的背后&#xff0c;是大模型参数指数级的增长。 这种数据暴涨的压力&#xff0c;快速传导到了大模…

Unity 3D定点数物理引擎实战系列:BEPU物理引擎碰撞计算与碰撞规则的架构与设计

前面我们讲解了如何监听物理引擎的碰撞事件, 在物理引擎内核中如何架构与设计碰撞规则,使得物理Entity与周围的物理环境产生碰撞时&#xff0c;如何灵活的控制物理碰撞&#xff0c;本节給大家详细的讲解BEPUphysicsint 物理引擎内部是如何管理与控制碰撞规则的。本文主要讲解3个…

HTML的超链接

前言&#xff1a; 如图&#xff0c;我们在浏览网页时经常可以看到这样的字体&#xff08;点击便跳转到了别的地方了&#xff09;&#xff0c;今日就和各位一起学习一下超链接的相关知识。 相关知识1&#xff1a; 超链接的标签为&#xff1a;a ~使用格式为&#xff1a; <a h…

node.js服务器静态资源处理

前言&#xff1a;node.js服务器动态资源处理见 http://t.csdnimg.cn/9D8WN 一、什么是node.js服务器静态资源&#xff1f; 静态资源服务器指的是不会被服务器的动态运行所改变或者生成的文件. 它最初在服务器运行之前是什么样子, 到服务器结束运行时, 它还是那个样子. 比如平…

单元测试四大过程

单元测试四大过程&#xff08;蓝桥课学习笔记&#xff09; 单元测试过程 单元测试是软件测试过程中的一个关键环节&#xff0c;它与集成测试、系统测试一样&#xff0c;分为测试策划、测试设计、测试执行和测试总结几个阶段。 单元测试过程中每个阶段需要完成的主要工作如下&…

数据结构排序算法

排序也称排序算法(SortAlgorithm)&#xff0c;排序是将一组数据&#xff0c;依指定的顺序进行排列的过程。 分类 内部排序【使用内存】 指将需要处理的所有数据都加载到内部存储器中进行排序插入排序 直接插入排序希尔排序 选择排序 简单选择排序堆排序 交换排序 冒泡排序快速…

用html写文本变形动画

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>文本变形动画</title><link rel"stylesheet" href"./style.css"> </head> <body> <!-- 两个文本部分…

广东莱斯广告,6.8米UV喷印推动粤东喷绘产业升级

广东莱斯广告作为汕头市大型的广告服务运营商,近日迎来了一件值得庆祝的事情:彩神6.8米UV喷印机运行一周年,销售服务商深圳嘉豪总经理李伟特地前来回访。该设备是深圳润天智数字设备股份有限公司开发的全球首台搭载XTRA6800H柯尼卡喷头的设备,设备特点是:1.色彩艳丽;2.超宽喷印…

记录flume运行时报NullPointerException异常

【背景说明】 我要起一个将kafka上的topic_log主题中的数据上传到hdfs上的flume进程。 这是我的flume配置文件脚本&#xff1a; #定义组件 a1.sourcesr1 a1.channelsc1 a1.sinksk1#配置source1 a1.sources.r1.type org.apache.flume.source.kafka.KafkaSource a1.sources.r…

《Kubernets证书篇:基于Kylin V10+ARM架构CPU修改K8S 1.26.15版本证书时间限制》

一、背景 Kubernetes 默认的证书有效期只有1年&#xff0c;因此需要每年手动更新一次节点上面的证书&#xff0c;特别麻烦而且更新过程中可能会出现问题&#xff0c;因此我们要对 Kubernetes 的 SSL 证书有效期进行修改&#xff0c;这里将证书的时间限制修改为100年。 环境信息…

112 arcpy 发布 mxd地图文件 到 arcgis服务器 为 地图服务

前言 此文档主要是记录一下 最近的一次机遇 arcpy 来发布 地图文件到 arcgis服务器 上面 arcpy 主要是来自于 ArcGIS_Desktop_105_154030.zip 安装之后会在 python 的安装目录 安装另外的一份带 arcgis 的 python 环境, 然后 本文相关类库 也是基于 这个 arcpy 的 python 环境…

web实战项目环境部署(LNMP)

环境搭建准备 1、在本机安装VMware虚拟机&#xff0c;实际工作中&#xff0c;使用的是云服务器 2、在虚拟机上安装并运行Linux系统 3、在本机上安装好远程连接工具&#xff08;xshell/FinalShell&#xff09;&#xff0c;通过远程连接工具连接到虚拟机 4、关闭linux上的防火…

深度学习之PyTorch实现卷积神经网络(CNN)

在深度学习领域&#xff0c;卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;CNN&#xff09;是一种非常强大的模型&#xff0c;专门用于处理图像数据。CNN通过卷积操作和池化操作来提取图像中的特征&#xff0c;具有较好的特征学习能力&#xff0c;特别适用…

云原生:企业数字化转型的引擎与未来

一&#xff0c;引言 随着信息技术的飞速发展&#xff0c;企业数字化转型已成为时代的必然趋势。在这场深刻的变革中&#xff0c;云原生技术以其独特的优势&#xff0c;逐渐成为推动企业数字化转型的核心动力。本文将详细探讨云原生技术的内涵、发展历程&#xff0c;以及在企业数…

【Java开发指南 | 第八篇】Java变量、构造方法、创建对象

专栏&#xff1a;Java开发指南 CSDN秋说 文章目录 Java变量构造方法创建对象 Java变量 局部变量&#xff1a;在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中&#xff0c;方法结束后&#xff0c;变量就会自动销毁。成员变量&#xff08;…

研究生,该学单片机还是plc。?

PLC门槛相对较低&#xff0c;但是在深入学习和应用时&#xff0c;仍然有很高的技术要求。我这里有一套单片机入门教程&#xff0c;不仅包含了详细的视频 讲解&#xff0c;项目实战。如果你渴望学习单片机&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&am…

OpenHarmony实战开发-图片选择和下载保存案例。

介绍 本示例介绍图片相关场景的使用&#xff1a;包含访问手机相册图片、选择预览图片并显示选择的图片到当前页面&#xff0c;下载并保存网络图片到手机相册或到指定用户目录两个场景。 效果图预览 使用说明 从主页通用场景集里选择图片选择和下载保存进入首页。分两个场景点…