HDU5812 Distance(枚举 + 分解因子)
題目
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(枚举 + 分解因子)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于启动 SecureCRT 遇到一个致
- 下一篇: 8.8-8.10 usaco