解题报告(2)——动态分班

解题报告(2)——动态分班

动态分班

题目描述

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

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

算法分析:

方法一: 打表+并查集

1)打表找出2到100000的全部质数,定义在数组里。

2)找出a到b范围内的每一个质数的第一个倍数。

3)并查集合并该质数a到b范围内的全部倍数。

4)统计集合个数。

 

Source:

方法筛选法+并查集

1)用筛选法找出2到b的全部质数,收录在数组里。

2)找出a到b范围内的每一个质数的第一个倍数。

3)并查集合并该质数a到b范围内的全部倍数。

4)统计集合个数。


Source:

Source:
#include
#include
#include
#include
#include
#include
 
using namespace std;
 
int a,b,p,d[100001],father[100001],cnt;
/*a,b:数据范围; p:质数范围*/
/*farher:并查集实现,当前所在集合; cnt:统计质数个数*/
bool f[100001],an[100001];
/*f[i]:筛选法应用,判断是否为质数; an[i]:判断该集合是否出现过*/ 
 
inline void R(int &v) /*读入优化*/
{
      v=0;
      char c=0;
      bool p=true;
      while(c>'9'||c<'0')
      {
            if(c=='-')
            {
                  p=false;
            }
            c=getchar();
      }
      while(c<='9'&&c>='0')
      {
            v=(v<<3)+(v<<1)+c-'0';
            c=getchar();
      }
      if(p==false)
      {
            v=-v;
      }
}
 
int getfather(int x) /*并查集核心代码*/
{
      if(father[x]==x)
      {
            return x;
      }
      else
      {
            father[x]=getfather(father[x]); /*路径压缩+答案查询*/
      }
      return father[x];
}
 
void unite(int x,int y)  /*合并两个集合*/
{
      int fa1,fa2;
      fa1=getfather(x);
      fa2=getfather(y);
      if(fa1!=fa2)
      {
            father[fa2]=fa1;
      }
}
 
int main(void)
{
      R(a);
      R(b);
      R(p);
      memset(f,true,sizeof(f)); /*初始化质数判断*/ 
      f[1]=false; /*1不为质数,单独处理*/
      for(int i=2;i<=b;++i) /*筛选法求质数*/
      {
            if(f[i]==true)  /*没有之前的因数,则为质数*/
            {
                  cnt++;     /*个数统计加1*/
                  d[cnt]=i;  /*记录*/
                  for(int j=2;j<=b/i;++j) /*将该数的倍数在b以内的筛掉*/
                  {
                        f[i*j]=false;
                  }
            }
      }
      for(int i=a;i<=b;++i) /*初始化父亲的值*/
      {
            father[i]=i;
      }
      for(int i=1;i<=cnt;++i) /*枚举质数*/
      {
            if(d[i]>=p) /*若出现在范围内*/
            {
                int y;
                  y=a/d[i]*d[i];
                  if(y


解题报告(2)——动态分班_第1张图片

Summary

本题从难度上而言,并不算太难,但是涉及到的知识较多,并且处理起来也容易因找不到合适方法而做过多无用功增加复杂度。两种方法比较,显然后者是标准,但是有时若在重要考试中出现问题,也可以采取第一种打表方式,先编写程序求出相应的值,在做处理,可以节约时间,从而不影响后面的计算和考虑。当然,必要的算法积累也很重要。




你可能感兴趣的:(NOIP解题报告,并查集,线性筛)