2016级算法第二次上机-F.ModricWang's Number Theory II
891 ModricWang's Number Theory II
思路
使得序列的最大公约数不为1,就是大于等于2,就是找到一个大于等于2的数,它能够整除序列中的所有数。
考虑使得一个数d整除数组中所有数的代价:
如果一个数不能被b整除,那么可以花费x的代价删掉它,或者通过多次加1使得它可以被d整除,代价应该为 \((d - a[i]\%d) * y\) , \((a[i] \% d == 0s时特判,应该为0)\)
令 \(l = x / y\)
如果\(d - a[i] \% d <= l\) \((a[i]\%d != 0)\), 这个数产生的代价是 \((d - a[i] \% d) * y\) , 否则是\(x\)。
所有代价求和就是总代价,最小的总代价就是答案。
但是这样枚举了d和a[i], 复杂度是\(O(n^2)\) 的。
考虑将a[i]换一种方式存储:b[i]表示值为i的数出现的次数。
这样d可以将b分成如下若干段:
\([0, d - 1], [d, d * 2 - 1], [d * 2, d * 3 - 1], ... ,[d * i, d * (i + 1) - 1]\)
对于每一段而言,
\([d * (i + 1) - l, d * (i + 1) - 1]\) 内的数应该通过多次加1变成\(d * (i + 1)\) ,
代价应为 \((该区间内数的个数 * d * (i + 1) - 该区间内的数之和) * y\)
\([d * i + 1 , d * (i + 1) - l - 1]\) 内的数应该直接删除,
代价应为 \(该区间内的个数 * x\)
通过构造相应的前缀和数组,上述操作均可以在\(O(1)\) 的时间复杂度内完成
具体操作时应该注意边界
因为合数会被质数整除,因此d可以只枚举质数。
计算时间复杂度需要一些数论知识。首先素数密度(也就是 \(\frac{小于n的素数}{n}\) )可以参见oeis A006880,一个近似解析式为 \(\frac{1}{ln(n)}\),那么\(小于n的素数的总个数\)可以近似为 \(\frac{n}{ln(n)}\)
设小于等于n的素数为\(prime[i]\),素数总数为\(P\),取近似\(P=\frac{n}{ln(n)}\)
求结果部分的复杂度可以写为 \(\sum_{1}^{P} \frac{n}{prime[i]}\)
参见wikipedia,素数的倒数和又可以近似为 \(\sum_{1}^{p} \frac{1}{prime[i]}=ln(ln(n))\)
因此 \(\sum_{1}^{P} \frac{n}{prime[i]} = O(n* ln(ln(n)))\)
这里得到了计算结果部分的复杂度,还需要加上求素数这个过程的时间复杂度。如果使用朴素筛法,求复杂度的过程正好的上文所述的完全一致,其复杂度为\(O(n*ln(ln(n)))\)。如果使用欧拉筛求素数,复杂度为\(O(n)\)。
因此\(O(运行时间)=O(求素数)+O(计算结果)=O(n*ln(ln(n)))\)
代码
#include<iostream>
#include<cstring>
using namespace std;
const long long Max_Ai = 1000000*2;
long long n, x, y, l;
long long nums[Max_Ai + 10];
long long s[Max_Ai + 10], sum[Max_Ai + 10];
bool valid[Max_Ai + 10];
long long prime[Max_Ai + 10];
long long tot;
//线性筛求素数
void init_prime() {
memset(valid, true, sizeof(valid));
for (int i = 2; i <= Max_Ai; i++) {
if (valid[i]) prime[++tot] = i;
for (int j = 1; j <= tot && i*prime[j] <= Max_Ai; j++) {
valid[i*prime[j]] = false;
if (i%prime[j]==0) break;
}
}
}
int main() {
#ifdef ONLINE_JUDGE
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#endif
init_prime();
cin >> n >> x >> y;
l = x/y;
for (long long i = 1; i <= n; i++) {
long long p;
cin >> p;
nums[p]++; //这是一种比较特别的数字记录方法,原理类似于基数排序radix sort
}
for (long long i = 1; i <= Max_Ai; i++) {
s[i] = s[i - 1] + nums[i]; //数量和
sum[i] = sum[i - 1] + nums[i]*i; //前缀和
}
auto min_cost = (long long) 1e18;
for (long long i = 1; i <= tot; i++) {
long long k = prime[i];
long long now_cost = 0;
for (long long j = 0; j <= Max_Ai; j += k) {
long long mid = max(j + k - l - 1, j);
long long bound = min(j + k - 1, Max_Ai);
if (bound > mid) {
now_cost += ((s[bound] - s[mid])*(j + k) - (sum[bound] - sum[mid]))*y;
now_cost += (s[mid] - s[j])*x;
} else {
now_cost += (s[bound] - s[j])*x;
}
}
min_cost = min(min_cost, now_cost);
}
cout << min_cost << "\n";
return 0;
}
2016级算法第二次上机-F.ModricWang's Number Theory II的更多相关文章
- 2016级算法第二次上机-G.ModricWang's Real QuickSort
873 思路 这是一道非常基础的题,目的是帮助大家回顾快排相关的知识.大家完成此题之后应该就对快排有比较深刻的印象了. 对于整个快排的流程,题目描述中已经给了清晰完整的伪代码.需要自己加工的部分就是, ...
- 2016级算法第二次上机-E.AlvinZH的儿时梦想——运动员篇
862-AlvinZH的儿时梦想--运动员篇 思路 难题. 应该想到,不管给出的数据如何,每一个淘汰的人不会对最终答案产生任何影响,所以每次淘汰就把人除掉就可以了,最后剩下的两个人计算它们从开始到相遇 ...
- 2016级算法第二次上机-D.Bamboo的饼干
Bamboo的饼干 分析 从两个数组中各取一个数,使两者相加等于给定值.要注意去重和排序 难度不大,方法很多,基本只要不大于O(n^2 ) 的都可以过.本意想考察二分搜索 还可以借助stl中的map, ...
- 2016级算法第二次上机-C.AlvinZH的儿时梦想——坦克篇
872 AlvinZH的儿时梦想----坦克篇 思路 简单题.仔细看题,题目意在找到直线穿过的矩形数最小,不能从两边穿过.那么我们只要知道每一行矩形之间的空隙位置就可以了. 如果这里用二维数组记住每一 ...
- 2016级算法第二次上机-B.Bamboo的OS实验
Bamboo的OS实验 分析 首先理解题意,要完成不同数量的不同命令,但是完成相同的命令之间必须有n个间隔,为使得时间最短,自然优先用其他命令来填充这n分钟的时间,由于数量少的命令可以用来填充空隙,所 ...
- 2016级算法第二次上机-A.画个圈圈诅咒你
890 画个圈圈诅咒你 思路 简单题.题目中的圆并没有什么实际作用,简化成线段重合问题会更好理解些. 暴力解法:使用双重for循环会T到想哭,记住最直接的方法一般是过不了题的. 解法一:二分查找.空间 ...
- 2016级算法期末模拟练习赛-F.AlvinZH的青春记忆IV
1086 AlvinZH的青春记忆IV 思路 难题,动态规划. 这是一道很有意思的题,因为它不仅卡了时间,也卡了空间,而且卡的很妙很迷. 光是理解题意已经有点难度,简化题意:两串数字序列,相等的数字定 ...
- 题解:2018级算法第二次上机 Zexal的流水线问题
题目描述: 样例: 实现解释: 最基础的流水线调度问题,甚至没有开始和结束的值 实现方法即得出状态转移方程后完善即可,设a[][i]存储着第一二条线上各家的时间花费,t[][i]存储着i处进行线路切换 ...
- 题解:2018级算法第二次上机 Zexal的排座位
题目描述: 样例: 实现解释: 一道看似复杂但实际既是斐波那契变形的题目 知识点:递推,斐波那契 通过问题的描述,可以得到以下规律:(除了座位数为一时)男生坐最后时,倒数第二个一定是女生:女生坐最后, ...
随机推荐
- redis 面试题1 有用
1.什么是redis? Redis 是一个基于内存的高性能key-value数据库. 2.Reids的特点 Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库 ...
- 文件操作getc
getc函数的作用是从打开的文件中获取一个字符,并加文件指针自动加1,获取的字符在返回值中. 我写了一个读取一个文件255个字节的程序. int main() { FILE *p; fopen_s(& ...
- 循环结构之for循环
循环结构之for循环(一) 在很多编程语言中都有一种直接.简单的循环,它的一般形式为: 它的执行过程如下: 第一步:执行表达式1,对循环变量做初始化: 第二步:判断表达式2,若其值为真(非0),则执行 ...
- 十万个为什么:现在还没发现“虚函数virtual”和多态性的优点,估计是因为我还没有编程序吧。
十万个为什么:现在还没发现“虚函数virtual”和多态性的优点,估计是因为我还没有编程序吧.
- 使用python管理Cisco设备-乾颐堂
今天发现一个老外使用python写的管理cisco设备的小框架tratto,可以用来批量执行命令. 下载后主要有3个文件: Systems.py 定义了一些不同设备的操作系统及其常见命令. Conne ...
- SSL与TLS有什么区别
SSL与TLS有什么区别(最全面的知识点都在这) 发布日期:2018-10-12SSL:(Secure Socket Layer,安全套接字层),位于可靠的面向连接的网络层协议和应用层协议之间的一种协 ...
- 反射机制:获取class的方法
- linux的操作
对tomcat的操作 打开终端 cd /java/tomcat#执行bin/startup.sh #启动tomcatbin/shutdown.sh #停止tomcat tail -f logs/cat ...
- 改善C#公共程序类库质量的10种方法(转)
出处:http://www.cnblogs.com/JamesLi2015/p/3140897.html 最近重构一套代码,运用以下几种方法,供参考. 1 公共方法尽可能的使用缓存 public s ...
- 【转载】SQL注入攻防入门详解
滴答…滴答…的雨,欢迎大家光临我的博客. 学习是快乐的,教育是枯燥的. 博客园 首页 博问 闪存 联系 订阅 管理 随笔-58 评论-2028 文章-5 trackbacks-0 站长 ...