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

这是一篇关于C++解决动态分班问题的博客,介绍了如何处理具有公共质因数的学生编号重组,最终确定班级数量。通过枚举质数和使用并查集压缩路径的方法,实现了在O((B-A)*6)的时间复杂度内求解。样例解释展示了具体操作过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态分班

题目描述

某中学对班级实行动态管理,每学年结束后都要重新分配班级,但这所学校重新分配的方法和石室中学完全不同。

现在给出一些属于同一年级学生的连续编号,它们都是从 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值