HDU 5812 Distance

题目: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:\n",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\n");
145         else printf("%d\n",mint);
146       }
147     }
148   }
149   return 0;
150 }
posted @ 2016-08-10 18:41  hchlqlz  阅读(390)  评论(0编辑  收藏  举报