题目

Source

http://acm.hdu.edu.cn/showproblem.php?pid=5812

Description

In number theory, a prime is a positive integer greater than 1 that has no positive divisors other than 1 and itself. The distance between two positive integers x and y, denoted by d(x, y), is defined as the minimum number of multiplications by a prime or divisions (without a remainder) by a prime one can perform to transform x into y. For example, d(15, 50) = 3, because 50 = 15 * 2 * 5 / 3, and you have to perform two multiplications (*2, *5) and one division (/3) to transform 15 into 50.

For a set S of positive integers, which is initially empty, you are asked to implement the following types of operations on S.

1. I x: Insert x into S. If x is already in S, just ignore this operation.
2. D x: Delete x from S. If x is not in S, just ignore this operation.
3. Q x: Find out a minimum z such that there exists a y in S and d(x, y) = z.

Input

The input contains multiple test cases. The first line of each case contains an integer Q (1 <= Q <= 50000), indicating the number of operations. The following lines each contain a letter ‘I’, ‘D’ or ‘Q’, and an integer x (1 <= x <= 1000000).
Q = 0 indicates the end of the input.
The total number of operations does not exceed 300000.

Output

For each case, output “Case #X:” first, where X is the case number, starting from 1. Then for each ‘Q’ operation, output the result in a line; if S is empty when a ‘Q’ operation is to perform, output -1 instead.

Sample Input

12
I 20
I 15
Q 30
I 30
Q 30
D 10
Q 27
I 15
D 15
D 20
D 30
Q 5
0

Sample Output

Case #1:
1
0
3
-1

分析

题目大概说,定义d(x,y)为x通过乘或除以质数变为y的最少运算次数。现在有一个集合,有插入一个数到集合的操作,也有从集合中删除一个数的操作,还有查询操作:输出最小的d(a,b),a是所查询的数,b是集合中的任一数。

题解这么说的:

不难发现d(a, b) = f(a/gcd(a, b)) + f(b/gcd(a,b)),其中f(x)表示x的质因子个数. 因而当遇到操作Q x时,我们只需要枚举x的每个约数y,看属于当前集合的y的所有倍数z中f(z/y)的最小值为多少. 为了快速求出这个最小值,我们用C[y][s]表示当前集合中y的所有倍数z中使得f(z/y)=s的z的数量. 因为s的值不会超过20,所以可以用位压缩的方法,用D[y]表示y的倍数中哪些s值出现了,这样查询最小的s值可以通过位运算快速求出(因为时限是标程的3倍,所以也不会特意卡掉其它方法). 插入和删除x时同样可以通过枚举x约数的方法来更新C[y][s]和D[y]的值. 设M表示元素的最大值,因为1到M所有约数的数量是O(MlogM)的,所以算法的时间和空间复杂度也都是O(MlogM)的. 又因为操作数少于M,所以实际情况还会更好一些.

首先是d(a, b) = f(a/gcd(a, b)) + f(b/gcd(a,b)),这个是显然的,而f(x)可以通过线性筛求得。

然后,对于每一个查询,枚举约数cd(注意,这个约数的个数在1000000内最多为128个,即2*3*5*7*11*13*17=510510的约数个数)。

  • f(a/cd)这个能求得;而对于f(b/cd),这个就需要在更新集合过程中做一些处理——

    • 插入数x到集合时,同样也是枚举数x的约数d,然后把f(x/d)的值更新到各个约数d的信息中。对于从集合中删除数的操作同样反过来做。
  • 于是对于各个cd,我们就能获得集合更新过程中最小的f(b/cd)。

更新集合维护各个约数最小的那个值,可以用官方题解说的那样,也能用网上其他题解用的multiset。

我都有尝试,代码见下。不过官方题解的做法我跑了1000多秒,写得太挫了吧。另外感觉,对一些东西都不敏感,约数个数,质因子个数等等其实都是很小的,没这种概念。

代码

multiset

#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;int prime_cnt[1000001],prime[1000001],pn;
bool vis[1000001];multiset<int> mset[1000001];void insert(int n){if(vis[n]) return;vis[n]=1;for(long long i=1; i*i<=n; ++i){if(n%i==0){int tmp=n/i;mset[i].insert(prime_cnt[tmp]);if(tmp!=i) mset[tmp].insert(prime_cnt[i]);}}
}
void remove(int n){if(!vis[n]) return;vis[n]=0;for(long long i=1; i*i<=n; ++i){if(n%i==0){int tmp=n/i;mset[i].erase(mset[i].find(prime_cnt[tmp]));if(tmp!=i) mset[tmp].erase(mset[tmp].find(prime_cnt[i]));}}
}
int query(int n){int res=11111111;for(long long i=1; i*i<=n; ++i){if(n%i==0){int tmp=n/i;if(!mset[i].empty()){res=min(res,prime_cnt[tmp]+*mset[i].begin());}if(tmp!=i && !mset[tmp].empty()) res=min(res,prime_cnt[i]+*mset[tmp].begin());}}if(res==11111111) return -1;return res;
}int main(){for(long long i=2; i<1000001; ++i){if(!vis[i]) prime[pn++]=i,prime_cnt[i]=1;for(int j=0; j<pn && i*prime[j]<1000001; ++j){vis[i*prime[j]]=true;prime_cnt[i*prime[j]]=prime_cnt[i]+1;if(i%prime[j]==0) break;}}int q,cse=0;while(~scanf("%d",&q) && q){printf("Case #%d:\n",++cse);memset(vis,0,sizeof(vis));for(int i=0; i<1000001; ++i) mset[i].clear();while(q--){char op; int a;scanf(" %c",&op); scanf("%d",&a);if(op=='I'){insert(a);}else if(op=='D'){remove(a);}else{printf("%d\n",query(a));}}}return 0;
}

官方题解

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;int prime_cnt[1000001],prime[1000001],pn;
bool vis[1000001];int C[1000001][20],D[1000001];void insert(int n){if(vis[n]) return;vis[n]=1;for(long long i=1; i*i<=n; ++i){if(n%i) continue;int j=n/i;++C[i][prime_cnt[j]];D[i]|=(1<<prime_cnt[j]);if(i!=j){++C[j][prime_cnt[i]];D[j]|=(1<<prime_cnt[i]);}}
}
void remove(int n){if(!vis[n]) return;vis[n]=0;for(long long i=1; i*i<=n; ++i){if(n%i) continue;int j=n/i;if(--C[i][prime_cnt[j]]==0) D[i]^=(1<<prime_cnt[j]);if(i!=j && --C[j][prime_cnt[i]]==0) D[j]^=(1<<prime_cnt[i]);}
}
int posi[1000001];
int query(int n){int res=1111111;for(long long i=1; i*i<=n; ++i){if(n%i) continue;int j=n/i;if(D[i]){res=min(res,prime_cnt[j]+posi[D[i]&-D[i]]);}if(D[j]){res=min(res,prime_cnt[i]+posi[D[j]&-D[j]]);}}if(res==1111111) return -1;return res;
}int main(){for(long long i=2; i<1000001; ++i){if(!vis[i]) prime[pn++]=i,prime_cnt[i]=1;for(int j=0; j<pn && i*prime[j]<1000001; ++j){vis[i*prime[j]]=true;prime_cnt[i*prime[j]]=prime_cnt[i]+1;if(i%prime[j]==0) break;}}for(int i=0; i<20; ++i){posi[1<<i]=i;}char op; int a;int q,cse=0;while(~scanf("%d",&q) && q){printf("Case #%d:\n",++cse);memset(vis,0,sizeof(vis));memset(C,0,sizeof(C));memset(D,0,sizeof(D));while(q--){scanf(" %c",&op); scanf("%d",&a);if(op=='I'){insert(a);}else if(op=='D'){remove(a);}else{printf("%d\n",query(a));}}}return 0;
}

转载于:https://www.cnblogs.com/WABoss/p/5759927.html

HDU5812 Distance(枚举 + 分解因子)相关推荐

  1. 阶乘分解质因数[经典题组合数学枚举质因子]

    引入问题: 给定整数NNN,试把阶乘 N!N!N! 分解质因数,按照算术基本定理的形式输出分解结果中的 pip_ipi​ 和 cic_ici​即可. N!N!N!分解质因数后的结果,共若干行,每行一对 ...

  2. c语言判断素数squ,poj1811——Prime Test//素数判断+整数分解因子

    题意:给定N,如果N为素数,输出"Prime",否则输出其最小因子. 思路:用miller_rabin判断素数,pollardRho用于整数因子的分解.整数因子分解还有一个更快的算 ...

  3. 【牛客 - 280C】约数(数论,GCD,数学,分解因子)

    题干: Actci上课睡了一觉,下课屁颠屁颠的去找数学老师补课,问了老师一个题目:     给出两个数a,b,问a和b的全部公约数是什么? 数学老师一看这道题太简单了,不屑回答,于是就交给了你. 输入 ...

  4. Codeforces 757B - Bash's Big Day(分解因子+hashing)

    757B - Bash's Big Day 思路:筛法.将所有因子个数求出,答案就是最大的因子个数,注意全为1的特殊情况. 代码: #include<bits/stdc++.h> usin ...

  5. Codeforces 1058 D. Vasya and Triangle 分解因子

    传送门:http://codeforces.com/contest/1058/problem/D 题意: 在一个n*m的格点中,问能否找到三个点,使得这三个点围成的三角形面积是矩形的1/k. 思路: ...

  6. LightOJ 1278 - Sum of Consecutive Integers 分解奇因子 + 思维

    http://www.lightoj.com/volume_showproblem.php?problem=1278 题意:问一个数n能表示成几种连续整数相加的形式 如6=1+2+3,1种. 思路:先 ...

  7. 因子分析模型(主成分解)、及与主成分分析模型的联系与区别(附详细案例)

    * * *  * *   * * * 因子分析是主成分分析的推广和发展,它也是多元统计分析中将为的一种方法. 因子分析是研究相关阵和或协方差阵的内部依赖关系,它将多个变量综合为少数几个因子,以再现原始 ...

  8. acm -(并查集、启发式合并、gcd、枚举因子)2020 China Collegiate Programming Contest Changchun Onsite K. Ragdoll

    传送门 本题考虑直接对每个iii求出所有满足ij=gcd(i,j)i^j=gcd(i,j)ij=gcd(i,j)的jjj,然后存在ggg数组中,对于查询修改操作维护一个并查集即可,合并的时候采用启发式 ...

  9. POJ 2429 GCD LCM Inverse ★(pollard-ρ DFS枚举)

    题目链接:http://poj.org/problem?id=2429 题目大意:给定gcd(a,b)和lcm(a,b)(<2^63),求a和b,如果有多种情况,输出和最小的情况.   首先gc ...

最新文章

  1. boost::range模块reversed相关的测试程序
  2. jquery插件之jquery-ui
  3. 认识计算机系统反思,《计算机系统组成》教学反思
  4. Win10错误代码0x80070541是怎么回事
  5. 【.NET】使用HtmlAgilityPack抓取网页数据
  6. Android实现Telnet客户端
  7. 120日均线金叉250日均线是大牛市来临的重要信号
  8. Web开发框架——Zheng
  9. 关于标志信息ZF、OF、SF、CF的理解
  10. 投影幕布尺寸计算器_投影安装高质量,全靠这个计算器
  11. web多媒体技术在视频编辑场景的应用
  12. win10 网络重置后WIFI不见了
  13. vscode开发小程序需要安装的插件集合
  14. TM1637数码管显示
  15. 某预约系统分析 某区公共自行车租车卡在线预约,关于如何提高成功概率
  16. 香料图片加名称及其作用
  17. 【机器学习】聚类【Ⅰ】基础知识与距离度量
  18. make: 放弃循环依赖 问题解决(3d 重建)
  19. 终于看完了马未都说家具收藏,很好看
  20. 解析 css3 transition:all 1s ease 1s

热门文章

  1. Oracle归档日志文件(Archive Log file)
  2. 免费下载思科CCNP 642-143考试题库
  3. 视网膜脱落相关知识(持续更新中)
  4. spring和springboot区别
  5. 中文分词工具jieba中的词性类型(转载)
  6. sas university edition在ubuntu中的使用
  7. gitkraken同步建立repository与github上的repository
  8. python实现最小二乘法(转)
  9. 机器学习(三)——朴素贝叶斯方法、SVM(1)
  10. python查找文字在图片中的位置_图片中的文字竟然能如此快速提取?OCR文字识别功能简直太强大了...