CSP历年复赛题-P2119 [NOIP2016 普及组] 魔法阵

news/2025/3/12 1:38:37/文章来源:https://www.cnblogs.com/jcwy/p/18236639

原题链接:https://www.luogu.com.cn/problem/P2119

题意解读:在一组数里找出所有的Xa,Xb,Xc,Xd的组合,使得满足Xa<Xb<Xc<Xd, Xb-Xa=2(Xd-Xc), Xb-Xa<(Xc-Xb)/3,并统计出每个数作为A,B,C,D出现的次数。

解题思路:

1、枚举(O(n^4))

首先想到的是通过4重循环枚举所有可能的Xa,Xb,Xc,Xd,然后判断是否符合需要满足的条件,如果符合,就将这一组Xa,Xb,Xc,Xd所谓有效数字累计到答案A[Xa]++, B[Xb]++, C[Xc]++, D[Xd]++,A,B,C,D数组记录各个魔法值作为A,B,C,D出现的次数。显然已知4个变量之间的等式,没有必要4重循环枚举,只需要枚举3个变量即可,所以此代码就不写了。

2、枚举(O(n^3))

根据题目要求,魔法值一共有40000个,但每个魔法值不超过15000,也就是有很多同样的魔法值,那么相同的魔法值在形成Xa,Xb,Xc,Xd的组合时效果是一样的,没必要一一枚举,可以考虑去重。

我们设int magic[M]存储每种物品的魔法值,int h[N]记录每种魔法值对应的物品数,vector<int> magic2为去重排序后的魔法值

h[N]是桶数组,一方面可以记录每种魔法值的数量,另一方面可以起到计数排序的作用,因此我们对去重排序后的魔法值magic2进行枚举即可。

先看Xa<Xb<Xc<Xd, 枚举是可以先枚举Xa,再枚举Xb,然后枚举Xc,Xd可以计算得来

再看Xb-Xa=2(Xd-Xc), 可以有两个结论:a、Xb-Xa是偶数,这样在枚举中可以提前判断 b、Xd = (Xb - Xa + 2 * Xc) / 2

最后Xb-Xa<(Xc-Xb)/3,经过移项可得:Xc > 4 * Xb - 3 * Xa,可以在枚举到Xc时进行提前判断

由于每种魔法值有多个,而枚举对象的已经去重之后的魔法值,所以在找到一组有效的Xa, Xb, Xc, Xd时,如何计算作为A,B,C,D出现的次数呢?

根据乘法原理:

Xa作为A出现的次数是由Xb,Xc,Xd的个数决定的,从h[Xb]个Xb中选1个,从h[Xc]个Xc中选1个,从h[Xd]个Xd中选1个都可以和Xa组成有效的组合,

所以有A[Xa] += h[Xb] * h[Xc] * h[Xd]

同理,

B[Xb] += h[Xa] * h[Xc] * h[Xd]
C[Xc] +=h[Xa] *h[Xb] *h[Xd]
D[Xd] +=h[Xa] *h[Xb] *h[Xc]
最后,按输入顺序枚举每一种魔法值,输出其作为A,B,C,D出现的次数
这样编写的代码就能得到90分,如果在考试中,够用了。

90分代码:

#include <bits/stdc++.h>
using namespace std;const int N = 15005, M = 40005;
int n, m;
int A[N], B[N], C[N], D[N]; //每种魔法值作为A、B、C、D的次数
int magic[M]; //每种物品的魔法值
int h[N]; //每种魔法值对应的物品数
vector<int> magic2; //去重排序后的魔法值int main()
{cin >> n >> m;for(int i = 1; i <= m; i++){cin >> magic[i];h[magic[i]]++;}for(int i = 1; i <= N; i++){if(h[i] > 0) magic2.push_back(i);}for(int i = 0; i < magic2.size(); i++) //枚举Xa{int Xa = magic2[i];for(int j = i + 1; j < magic2.size(); j++) //枚举Xb{int Xb = magic2[j];if((Xb - Xa) % 2 == 1) continue; //Xb-Xa=2(Xd-Xc),所以必须是偶数for(int k = j + 1; k < magic2.size(); k++) //枚举Xc{int Xc = magic2[k];if(Xc <= 4 * Xb - 3 * Xa) continue; //Xa、Xb、Xc要符合关系int Xd = (Xb - Xa + 2 * Xc) / 2; //根据公式计算Xdif(h[Xd]){//Xa,Xb,Xc,Xd魔法值作为A,B,C,D的个数,要取决于其他魔法值的个数,根据乘法原理相乘A[Xa] += h[Xb] * h[Xc] * h[Xd];B[Xb] += h[Xa] * h[Xc] * h[Xd];C[Xc] += h[Xa] * h[Xb] * h[Xd];D[Xd] += h[Xa] * h[Xb] * h[Xc];}}}}for(int i = 1; i <= m; i++){cout << A[magic[i]] << " " << B[magic[i]] << " " << C[magic[i]] << " " << D[magic[i]] << endl;}return 0;
}

3、枚举(O(n^2/9))

此题要得到100分,就需要换一种思维方式

已知:

Xa<Xb<Xc<Xd,

Xb-Xa=2(Xd-Xc),

Xb-Xa<(Xc-Xb)/3   =>   Xc-Xb > 3(Xb-Xa)  

我们设t = Xd-Xc,则有Xb-Xa=2t,Xc-Xb > 6t,所以Xa,Xb,Xc,Xd的大致分布为:

因此,我们在统计所有符合要求的Xc,Xd时只需要枚举两个变量:t、Xd

9t < n - 1,即9t + 2 <= n

Xd > Xa + 9t,所以 1 + 9t < Xd <= n

Xc = Xd - t

所以,从小到大枚举所有的t、Xd,Xc可以计算出来,而符合要求的Xa,Xb就是Xc,Xd左边所有能放得下的Xa,Xb

这里可以不用一一枚举xa,xb的位置,只需要限定Xb与Xc的距离,最小为6t+1,这样随着Xd,Xc的枚举右移,Xa,Xb也往右移动,

将Xa,Xb对结果的贡献用前缀和记录下来,每次更新

sum_ab += h[Xa] * h[Xb]

C[Xc] += h[Xd] * sum_ab

D[Xd] += h[Xc] * sum_ab

同理,在统计所有符合要求的Xa,Xb是只需要枚举两个变量:t、Xa

这次要从右往左枚举Xa

9t + 2 <= n

1<=Xa<=n-9t-1

sum_cd += h[Xc] * h[Xd]

C[Xc] += h[Xd] * sum_cd

D[Xd] += h[Xc] * sum_cd

100分代码:

#include <bits/stdc++.h>
using namespace std;const int N = 15005, M = 40005;
int n, m;
int A[N], B[N], C[N], D[N]; //每种魔法值作为A、B、C、D的次数
int magic[M]; //每种物品的魔法值
int h[N]; //每种魔法值对应的物品数
vector<int> magic2; //去重排序后的魔法值int main()
{cin >> n >> m;for(int i = 1; i <= m; i++){cin >> magic[i];h[magic[i]]++;}for(int i = 1; i <= N; i++){if(h[i] > 0) magic2.push_back(i);}for(int t = 1; 9 * t + 2 <= n; t++){int sum_ab = 0;for(int Xd = 9 * t + 1; Xd <= n; Xd++){int Xa = Xd - 9 * t - 1;int Xb = Xa + 2 * t;int Xc = Xd - t;sum_ab += h[Xa] * h[Xb];C[Xc] += h[Xd] * sum_ab;D[Xd] += h[Xc] * sum_ab;}int sum_dc = 0;for(int Xa = n - 9 * t - 1; Xa >= 1; Xa--){ //注意顺序int Xb = Xa + t * 2;int Xd = Xa + 9 * t + 1;int Xc = Xd - t;sum_dc += h[Xc] * h[Xd];A[Xa] += sum_dc * h[Xb];B[Xb] += sum_dc * h[Xa];}}for(int i = 1; i <= m; i++){cout << A[magic[i]] << " " << B[magic[i]] << " " << C[magic[i]] << " " << D[magic[i]] << endl;}return 0;
}

 

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

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

相关文章

应用解析 | 面向智能网联汽车的产教融合解决方案

经纬恒润融合二十多年的行业经验,将实际的工程应用、项目需求引入到产教融合中,打破院校与企业之间的界限,做到所学即所用,毕业即就业。 背景介绍随着科技的飞速发展,智能网联汽车已成为汽车产业的新宠,引领着未来出行的潮流。然而,行业的高速发展也带来了对高素质技术技…

PTA大作业4-6总结

前言 这三次大作业使用到的新知识点并不是很多,主要还是以继承与多态为主,虽然在新知识点上并没有增加许多,但明显在类的构造,类与类之间的关联设计方面难度明显有较大的增加,题量方面明显还是和之前三次大作业一样,每次大作业都是以一题为主,其余的题目都是为了巩固基础…

.NET之Hangfire快速入门和使用

原文地址:.NET之Hangfire快速入门和使用 - 追逐时光者 - 博客园 (cnblogs.com)前言:定时任务调度问题,是一个老生常谈的问题。网上有许多定时任务调度的解决方案,对于我而言很早以前主要是使用Window计划和Window服务来做任务定时执行,然后就开始使用定时任务调度框架Quar…

插值技术研究

过采样与欠采样&图像重采样(上采样&下采样) 研究图像插值技术:2016年苏州大学的钟宝江等人《图像插值技术综述》[1]图像插值利用图像已知采样点的灰度值估计未知采样点的灰度值,是图像数据的一种生成过程 。 图像插值的目的在于通过升采样的方式提高图像的分辨率。…

科研日记4【2024-06-06】

实验高度向稀疏采样造成的整行缺失使得在高度向上出现严重混叠现象, 对高度向稀疏采样的数据首先利用线性插值恢复补全,再将部分插值去掉,以模拟二维随机降采样。 实验结果说明上述方法并不好。 高度向50%稀疏直接成像:高度向50%稀疏-不动迭代成像:高度向50%稀疏-线性插值…

活动预热丨在 AGI Playground 2024 遇见一群 RTE+AI 的 Builders

6 月 22、23 日,北京。AGI Playground 2024,这个夏日最火热的 AGI 盛会。王小川、杨植麟等 AGI 创业者悉数参加。RTE 开发者社区的 builders 和 RTE Open Day 也将在现场!我们将为大家呈现两大板块: 01 实时开发挑战 Workshop RTE 开发者社区将联合「零一万物」发起 worksh…

Yearning外置工单通知实现思路

背景概述 上篇我们讲解了一下Yearning如何使用飞书发送工单通知,但是我最初的想法不仅仅是飞书、钉钉这些媒介,更多的是希望可以自定义集成渠道,因此想到了PrometheusAlert,这样我们就不用重新造轮子了。 大致配置image-20240606110115965 我们拿到Yearning发送的数据,当然…

一加七Pro刷Lineageos21(kernelsu+MicroG)

前言 前几天我已经刷了Lineageos21(一加七Pro刷lineageos21(kernelsu+gapps)),但是体验下来有两个很严重的bug:无法接打电话,没有声音。 收短信倒是很正常,但电话打进来只会显示号码,不会显示接听界面(对方那边显示正在通话中,也就是挂断了),拨打电话会显示已结束。 测试…

教务管理系统

1 项目简介 教务管理系统主要模块: 邮箱注册:用户根据邮箱发送验证码注册用户信息 邮箱登录:用户根据注册的邮箱登录 教师管理:主要包含教师的全部查询,教师信息的修改,添加教师信息,删除教师信息,分页查询教师信息 多条件查询教师信息(教师编号,名称,性别,入职日期…

网络隔离后的跨网投递需求,要这样做才能让需求落地

为了保护企业的核心数字资产、隔离有害的网络安全威胁、保障数据信息在可信网络内进行安全交互,越来越多的企业在网络建设时,选择进行网络隔离。应用较为普遍的网络隔离手段包括物理隔离、协议隔离、应用隔离等,而常见的状态是企业进行内部网络和外部互联网隔离,或者企业内…

数据库系列16:MyISAM与InnoDB的索引对比

相关文章 数据库系列:MySQL慢查询分析和性能优化 数据库系列:MySQL索引优化总结(综合版) 数据库系列:高并发下的数据字段变更 数据库系列:覆盖索引和规避回表 数据库系列:数据库高可用及无损扩容 数据库系列:使用高区分度索引列提升性能 数据库系列:前缀索引和索引长…

《UML基础、案例与应用》习题记录-第8章

部分习题,使用visio或plantuml,非正确答案,仅供参考,欢迎评论,谢绝转载。 第8章 状态图 1.状态图 2.状态图 3.状态图 欢迎大家评论交流,发现博文中存在的问题一定要留言哦