题目:Distance
链接:http://acm.hdu.edu.cn/showproblem.php?pid=5812
题意:定义d(x,y)是x 变成y 至少需要的操作次数,每一次操作可以乘以或除以一个质数(必须能整除)。现在,有一个集合s,初始为空,每一次有可能插入一个数(如果已经存在就无视),有可能删除一个数(如果没有就无视),有可能给出一个数x,问d(x,y)的最小值,y属于集合s,如果集合为空输出-1。
思路:
首先想到最普通的做法,对于每一次询问x,遍历集合中已经存在的数y,令num[i]为i 的质因子数目,那么d(x,y)=num[x/gcd(x,y)]+num[y/gcd(x,y)],就是要加上y 独有的质因子,再减去x 独有的质因子。这里,num数组可以预处理,但每一次询问就要遍历集合中所有元素,时间复杂度高达O(n^2),肯定是要超时的。
不过,从上面的做法可以得到启发,每一次询问x,我们可以枚举x 的约数g,然后对于集合中的y,d(x,y)=d(x,g)+d(g,y)。很容易想到,约数最多100万个,那么我们用d 数组保存每个约数到集合中的y 的最小变化次数(暂不考虑删除),然后枚举x 的所有约数(最多也就几十个),就可以计算出解,也就是 num[x/g]+d[g]。d 数组的维护就是在插入的时候进行的,比如插入x,枚举x 的所有约数g,d[g]=min(d[g],num[x/g])。当然,这是对付没有删除的情况下,有删除,肯定不能只保存最小的那个了。
不过,现在问题就很简单了,我们可以弄100万个类型为Node的优先队列,Node包含val(集合中已经存在的数),num(val到这个优先队列表示的约数i 的变化次数)排序方式就是num越小越好,再开一个标记数组u,初始化为0,u[i]=0就表示i 不存在,也就是说q[j]的队头,u[val]如果为0,那么说明val已经被删除,就不能充数,而是出队,继续判断下一个。
AC代码:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<map> 5 #include<queue> 6 using namespace std; 7 #define N 1000010 8 #define LL long long 9 #define INF 2147483647 10 int num[N]={0}; 11 int prim[N],po=0; 12 13 14 int gcd(int a,int b) 15 { 16 return b?gcd(b,a%b):a; 17 } 18 int min(int x,int y) 19 { 20 return x<y?x:y; 21 } 22 23 int gg[N],go; 24 pair<int,int> bt[100000]; 25 int bo; 26 void dfs(int i,int all) 27 { 28 if(i==bo) 29 { 30 gg[go++]=all; 31 return ; 32 } 33 int k=1; 34 for(int j=0;j<=bt[i].second;j++) 35 { 36 dfs(i+1,all*k); 37 k=k*bt[i].first; 38 } 39 } 40 41 void fun(int x) 42 { 43 bo=0; 44 int i=0; 45 while(i<po && prim[i]*prim[i]<=x) 46 { 47 int flag=0; 48 bt[bo].second=0; 49 while(x%prim[i]==0) 50 { 51 bt[bo].second++; 52 flag=1; 53 x/=prim[i]; 54 } 55 if(flag==1) bt[bo++].first=prim[i]; 56 i++; 57 } 58 if(x!=1) 59 { 60 bt[bo].first=x; 61 bt[bo++].second=1; 62 } 63 dfs(0,1); 64 } 65 bool u[N]; 66 struct Node 67 { 68 int num,val; 69 bool friend operator < (Node a,Node b) 70 { 71 return a.num>b.num; 72 } 73 }; 74 75 priority_queue<Node> q[N]; 76 int main() 77 { 78 num[1]=0; 79 for(int i=2;i<=N-10;i++) 80 { 81 if(!num[i]) 82 { 83 num[i]=1; 84 prim[po++]=i; 85 } 86 for(int j=0;j<=po && (LL)i*prim[j]<N;j++) 87 { 88 num[i*prim[j]]=num[i]+1; 89 } 90 } 91 int n,cas=1; 92 while(scanf("%d",&n)!=EOF) 93 { 94 if(n==0) break; 95 char t[10]; 96 int x; 97 printf("Case #%d: ",cas++); 98 memset(u,0,sizeof(u)); 99 while(n--) 100 { 101 scanf("%s%d",t,&x); 102 if(t[0]=='I') 103 { 104 if(u[x]==0) 105 { 106 go=0; 107 fun(x); 108 for(int i=0;i<go;i++) 109 { 110 Node tmp; 111 tmp.num=num[x/gg[i]]; 112 tmp.val=x; 113 q[ gg[i] ].push(tmp); 114 } 115 } 116 u[x]=1; 117 } 118 else if(t[0]=='D') 119 { 120 u[x]=0; 121 } 122 else 123 { 124 go=0; 125 fun(x); 126 int mint=INF; 127 for(int i=0;i<go;i++) 128 { 129 int mm=INF; 130 int m=gg[i]; 131 while(q[m].size()) 132 { 133 Node tmp=q[m].top(); 134 if(u[tmp.val]==0) q[m].pop(); 135 else 136 { 137 mm=tmp.num; 138 break; 139 } 140 } 141 if(mm==INF) continue; 142 mint=min(mint,num[x/gg[i]]+mm); 143 } 144 if(mint==INF) printf("-1 "); 145 else printf("%d ",mint); 146 } 147 } 148 } 149 return 0; 150 }