长沙理工大学第十三届程序设计竞赛--Dzzq的离散数学教室1

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

    离散数学中有种名叫“哈斯图”的东西。

    在这题中,你们需要计算的是一些正整数在偏序关系“整除”下的哈斯图的边数。用大白话讲,在偏序关系“整除”下的哈斯图,就是把一个个正整数看成一个个图的节点,某些节点之间有边。连边的规则是这样的:对于任意两个正整数a和b(a<b)来说,如果b%a==0,并且不存在一个正整数c(a<c<b),使得条件b%c==0和c%a==0同时成立,那么我们就在节点a和节点b之间连一条边。

    现在问题是,给你们2个数L,R(1<=L,R<=1e6)。求由L,L+1,L+2...R这R-L+1个正整数在偏序关系“整除”下的哈斯图的边数。

    比如L=1,R=4,节点的组合有(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)。组合(1,2),(1,3),(2,4)可以连边。(1,4)因为中间存在c=2,不符合连边条件。所以当L=1,R=4的时候,这个哈斯图有3条边。

输入描述:

多组输入,不超过1000组数据
每组数据一行,包含2个正整数L和R,中间由空格分开。

输出描述:

每组数据输出一行,包含一个整数表示哈斯图的边数。
示例1

输入

1 4
4 10
1 10

输出

3
2
11

根据题意,能连边的条件是:

(1)b为a的倍数。

(2)b != k1 * c , c != k2 * a 同时成立,消去c,即 b!= (k1*k2)a = k*a      [k1,k2为任意大于1的正整数)] 

    要使得不存在这样一个c, 则不能存在k1,k2使得b是a的k1*k2倍,故能推出k1*k2是一个素数 因为素数除了1和本身就不能拆分出其它两个因子k1与k2,使得乘积等于本身了。

综合上述分析,b只能是a的素数倍。

例如:

a = 1, b = 2时,倍数为2,是素数倍,此时k= 2,k1 ,k2只能为1和2,但找不出整数c使得 b = k1*c,c = k2 *a同时成立,此种情况可以连边。

a =1, b=3时,倍数为3,是素数倍,此时k= 3,k1 ,k2只能为1和3,但找不出整数c使得 b = k1*c,c = k2 *a同时成立,此种情况可以连边。

a = 1, b = 4时,倍数为4,非素数倍, 此时k = 4,能找出k1 = 2,k2 = 2 ,(此时 c=2) 来使得 b = k1*c,c = k2 *a 同时成立,此种情况便不能连边。


因此,本题的题目就转换成了在 [ L,R ] 区间中寻找a和b(b>a),并且b = P*a (P是素数,有2,3,5,7,11,13...)情况数。

因此,只需要依据p从小到大进行遍历存在L<=a<b<=R,b = P*a的情况即可。

 按照这个思路,我们来解 样例[1,10] (即 L=1,R=10)

当P = 2时,(a, b) = {(1,2), (2,4), (3,6), (4,8), (5,10)}       R/ P - L +1 = 5个。

当P = 3时,(a, b) = {(1, 3), (2, 6), (3,9)}            R/ P - L +1 = 3个。

当P = 5时,(a, b) = {(1, 5), (2,10)}         R/ P - L +1= 2个。

当P = 7时,(a, b) = {(1,7)}         R/ P - L +1 =1个。

当P = 11时,P都大于R了,显然后面都不会存在了。

因此 答案是 5+3+2+1 = 11.

对于在P确定时,求该种情况下的个数t时,t = (R / P) - (L - 1)是这样来的: 当R/P>=L时,就是能连边的条件。例如样例[4,10],P = 2时,a = 1,2,3,4,5(有5个能连边),但a要大于等于左边界,故a只能取4和5,即剔除了比L小的情况。这就是计算a在[1,10]中的个数R/P,再剔除掉a在[1,3]范围内的个数(L-1)。

代码如下:

#include <bits/stdc++.h>

using namespace std;

int ans,l,r,p[1000010];
bool boo[1000010];


int main()
{
    //预处理求出1e6内的素数
    for (int i = 2  ; i <= 1000000; i ++)
        if (!boo[i])
        for (int j = i+i; j <= 1000000; j += i)
            boo[j] = true;


    for (int i = 2 ; i <= 1000000; i ++)
        if (!boo[i])
            p[++ p[0]] = i;
    // p[i] 储存的是第i个素数 ,p[0]储存的是总共的素数个数


    for ( ; scanf("%d%d",&l,&r) != EOF; ){
        ans = 0;
        int t;//遍历每个素数情况下的个数,如p = 2时,t=5
        for (int i = 1; i <= p[0]; i ++){
            int t = r/p[i];
            if (t >= l) {
                    ans += t-l+1;
            }
            // 最大可除数字减去左界就是当前质数的可被除的数量
            else break;
        }
        printf("%d\n",ans);
    }
    return 0;
}


阅读更多
个人分类: ACM算法竞赛
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭