您的位置:首页 > 编程语言 > C语言/C++

C++——NOIP模拟题——动态分班

2017-04-04 14:42 211 查看


动态分班


题目描述

某中学对班级实行动态管理,每学年结束后都要重新分配班级,但这所学校重新分配的方法和石室中学完全不同。
现在给出一些属于同一年级学生的连续编号,它们都是从 A 到 B 的整数。一开始每个编号都属于各自不同的班(即一个班只有一个学生),然后学校将进行以下的调整:每次选择两个属于不同班的编号,如果这两个编号拥有大于或等于 P 的公共质因数,那么就把她们所在的班合并成一个班。反复上述操作,直到没有可以合并的班为止。
现在请你求出最后这个年级有多少个班?


输入格式

一行,三个整数 A,B,P,其中 A≤B≤100000,2≤P≤B。


输出格式

输出一个数表示最终班的个数。


样例数据 1

输入

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言