某中学对班级实行动态管理,每学年结束后都要重新分配班级,但这所学校重新分配的方法和石室中学完全不同。
现在给出一些属于同一年级学生的连续编号,它们都是从 A 到 B 的整数。一开始每个编号都属于各自不同的班(即一个班只有一个学生),然后学校将进行以下的调整:每次选择两个属于不同班的编号,如果这两个编号拥有大于或等于 P 的公共质因数,那么就把她们所在的班合并成一个班。反复上述操作,直到没有可以合并的班为止。
现在请你求出最后这个年级有多少个班?
某中学对班级实行动态管理,每学年结束后都要重新分配班级,但这所学校重新分配的方法和石室中学完全不同。
现在给出一些属于同一年级学生的连续编号,它们都是从 A 到 B 的整数。一开始每个编号都属于各自不同的班(即一个班只有一个学生),然后学校将进行以下的调整:每次选择两个属于不同班的编号,如果这两个编号拥有大于或等于 P 的公共质因数,那么就把她们所在的班合并成一个班。反复上述操作,直到没有可以合并的班为止。
现在请你求出最后这个年级有多少个班?
一行,三个整数 A,B,P,其中 A≤B≤100000,2≤P≤B。
输出一个数表示最终班的个数。
输入
10 20 3
输出
7
【样例说明】
样例解释:最后只有 7 个班:{10,20,12,15,18},{11},{13},{14},{16},{17},{19}
【数据规模】
80% 的数据 B≤1000;
100% 的数据 B≤100000。
解题报告:
可以暴力判断两个点之间是否有边,然后求图的联通块个数,可以过80%。
一个数最多可能拥有的不同质数的个数是6个,2*3*5*7*11*13=30030。
我们可以枚举范围内所有合理的质数,对于质数p,我们可以把p的倍数的所有数合成一个集合。由于一个数最多有6个质数,所以每个数最多被找到6次。统计的时候,我们可以对每个质数建一个点,然后和属于它倍数的数连边,最后求出联通块的个数就可以了。
用并查集压缩路径;用筛法求素数。
时间复杂度:O((B-A)*6)
附录:
递归压缩路径可能会造成溢出栈,非递归写法如下:
int find(int x)
{
int k, j, r;
r = x;
while(r != parent[r]) //查找跟节点
r = parent[r]; //找到跟节点,用r记录下
k = x;
while(k!=r) //非递归路径压缩操作
{
j = parent[k]; //用j暂存parent[k]的父节点
parent[k] = r; //parent[x]指向跟节点
k = j; //k移到父节点
}
return r; //返回根节点的值
}
#include<stdio.h>
int a,b,p,o,t,ans;
int c[100001],father[100001];
bool fil[100001],h[100001];
inline int getfather(int v)
{
if(v==father[v]) return v;
else father[v]=getfather(father[v]);
return father[v];
}
int main()
{
fil[1]=true;
scanf("%d%d%d",&a,&b,&p);
for(int i=2;i<=b;++i)
if(!fil[i])
{
fil[i]=false;
c[0]++;
c[c[o]]=i;
for(int j=i;j<=b/i;++j)
fil[i*j]=true;
}
for(int i=a;i<=b;++i) father[i]=i;
for(int i=1;i<=c[0];++i)
if(c[i]>=p)
{
t=a/c[i]*c[i];
while(t<a) t+=c[i];
o=t;
while(t<=b)
{
father[getfather(t)]=getfather(o);
t+=c[i];
}
}
for(int i=a;i<=b;++i)
{
t=getfather(i);
if(!h[t])
{
h[t]=true;
++ans;
}
}
printf("%d\n",ans);
return 0;
}