题目

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;
}

HDU5812 Distance(枚举 + 分解因子)的更多相关文章

  1. uva 993 Product of digits (贪心 + 分解因子)

      Product of digits  For a given non-negative integer number N , find the minimal natural Q such tha ...

  2. D. Almost All Divisors(数学分解因子)

    其实这题并不难啊,但是分解因子的细节一定要小心. \(比如样例48,2是因子说明24也是因子,也就是说假如x存在\) \(那么x一定是因子中的最小数乘上最大数\) \(那我们现在去验证x是否存在,先拿 ...

  3. Codeforces 757B:Bash's Big Day(分解因子+Hash)

    http://codeforces.com/problemset/problem/757/B 题意:给出n个数,求一个最大的集合并且这个集合中的元素gcd的结果不等于1. 思路:一开始把素数表打出来, ...

  4. HDU5812 Distance 构造,预处理

    分析:怎么看都是超时,但是可以先筛一遍1e6以内的每个数的最小素数 算出每个数由多少个素数组成,然后应用,c[1e6][20] 就是题解的那一套,参照题解,比赛的时候没有想到好的办法筛一个数的因子,醉 ...

  5. hdu2588-GCD-(欧拉函数+分解因子)

    The greatest common divisor GCD(a,b) of two positive integers a and b,sometimes written (a,b),is the ...

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

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

  7. bzoj 1485 卡特兰数 + 分解因子

    思路:打表可以看出是卡特兰数,但是模数不一定是素数,所以需要分解一下因数. #include<bits/stdc++.h> #define LL long long #define fi ...

  8. HDU 4910 Problem about GCD 找规律+大素数判断+分解因子

    Problem about GCD Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  9. Light OJ 1318 Strange Game 组合数+高速幂+分解因子

    长度为l的用k种字符组成的字符串有k^l中 当中m个字符要不同样 那就是k^l*C(l, m)*(k-1)^m 有反复 要除以2 可是你mod n了 不能直接除 n不一定是素数 所以不能乘以逆元 所以 ...

随机推荐

  1. [Android] 深入浅出Android App耗电量统计

    reference to : http://www.cnblogs.com/hyddd/p/4402621.html 前言 在Android统计App耗电量比较麻烦,直至Android 4.4,它仍没 ...

  2. c#三层架构登陆实例

    很早之前,就听说过三层结构了.当时只知道 三层结构 是把 系统的 界面  跟 数据库操作等不相关的程序分别开来.原来这么简单的实现,确实传说中的 三层结构啊. 首先,先来看一下是哪三层.表示层(UI, ...

  3. IPC进程通信机制

    select.poll.epoll之间的区别总结[整理] 进程间通信---共享内存 信号量和互斥锁的区别 http://www.2cto.com/os/201510/445553.html http: ...

  4. valgrind检查C++内存泄漏

    valgrind --tool=memcheck --leak-check=full ./httptest Valgrind 使用 用法: valgrind [options] prog-and-ar ...

  5. entOS7安装iptables防火墙,试验未通过

    CentOS7默认的防火墙不是iptables,而是firewalle. 安装iptable iptable-service #先检查是否安装了iptables service iptables st ...

  6. Android下拉刷新效果实现

    本文主要包括以下内容 自定义实现pulltorefreshView 使用google官方SwipeRefreshLayout 下拉刷新大致原理 判断当前是否在最上面而且是向下滑的,如果是的话,则加载数 ...

  7. JavaScript基础——使用数组

    Array对象提供存储和处理一组其他对象的一种手段.数组可以存储数值.字符串或其他JavaScript对象.创建JavaScript数组有几种不同的方法.例如,下面的语句穿件同样的驻足的3个相同的版本 ...

  8. ***LINUX添加PHP环境变量:CentOS下将php和mysql命令加入到环境变量中

    CentOS系统下如何将PHP和mysql命令加入到环境变量中,在Linux CentOS系统上 安装完php和MySQL后,为了使用方便,需要将php和mysql命令加到系统命令中,如果在没有添加到 ...

  9. 无废话Android之listview入门,自定义的数据适配器、采用layoutInflater打气筒创建一个view对象、常用数据适配器ArrayAdapter、SimpleAdapter、使用ContentProvider(内容提供者)共享数据、短信的备份、插入一条记录到系统短信应用(3)

    1.listview入门,自定义的数据适配器 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/and ...

  10. java中常用的工具类(二)

    下面继续分享java中常用的一些工具类,希望给大家带来帮助! 1.FtpUtil           Java   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...