POJ 3090 Visible Lattice Points 法雷级数

本文介绍了法雷级数的概念及其与欧拉函数之间的联系。通过递推公式求解不同阶法雷级数中真分数的数量,并利用欧拉函数进行计算。此外,还探讨了法雷级数的一些有趣性质。

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

题意:从原点看第一象限里的所有点,能直接看到的点的数目是多少。(不包含原点)

 

法雷级数定义  R.亨斯贝尔格著李忠翻译的《数学中的智巧》一书,介绍了法雷级数。这里每一行从0/1开始,以1/1结尾,其它数自左至右将所有的真分数按增加顺序排列;第n行是由所有分母小于或等于n的真分数组成,我们称为n阶法雷级数。如下表:

  F1: 0/1 1/1

  F2: 0/1 1/2 1/1

  F3: 0/1 1/3 1/2 2/3 1/1

  F4: 0/1 1/4 1/3 1/2 2/3 3/4 1/1

  F5: 0/1 1/5 1/4 1/3 2/5 1/2 3/5 2/3 3/4 4/5 1/1

  F6:0/1 1/6 1/5 1/4 1/3 2/5 1/2 3/5 2/3 3/4 4/5 5/6 1/1

  …… ………………………………

  这里我们想问的是第n行Fn的真分数的个数有多少个呢?

  我们设Fn的个数为ψ(n), ψ(n)比 ψ(n-1)增加的个数是分母是n,分子比n小且与n互质的数的个数,这正是欧拉函数φ(n)。即

  ψ(n)=ψ(n-1)+ φ(n)

  ψ(1)=1+φ(1)

  ψ(2)=ψ(1)+φ(2)

  ψ(3)=ψ(2)+φ(3)

  ………………

  ψ(n)= ψ(n-1)+ φ(n)

  所以 ψ(n)=1+φ(1)+φ(2)+φ(3)+……+φ(n)很容易证明,当n≥3时,欧拉函数φ(n)是个偶数。由此我们得到除ψ(1)=2是偶数外,法雷级数其它各级的个数都是奇数,并且许多是素数。ψ(1)=2,ψ(2)=3,ψ(3)=5,ψ(4)=7,ψ(5)=11,ψ(6)=13,ψ(7)=19,ψ(8)=23,ψ(9)=29,……。

性质

  法雷级数Fn具有很多美妙的性质,下面是一些常见的性质:

  1.如果a/b,c/d是相邻的两项,则abs(a*d-b*c)=1。

  2.如果a/b,c/d,e/f是相邻的三项,则 (a+e)/(b+f)=c/d,特别的,如果c/d是新添加的,即c/d不属于F(n-1),则c=a+e;d=b+f。

  性质2对于这个问题至关重要,它的证明可以参见哈代(Hardy)写的数论导引第三章

  关于Farey级数的介绍。根据这条性质可以知道,丛F(n−1)到F(n)的构造过程中,F(n)的新项的分母一定是其相领两项的分母和。另一方面,如果F(n−1)中的相邻两项 a/b,c/d, b+d=n,则(a+c)/n一定会被添加到F(n)中。

 

#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 1009
int p[MAX], a[MAX], pn;
int eul[MAX];

void prime()
{
    pn = 0;
    memset(a,0,sizeof(a));
    int i, j;
    for ( i = 2; i < MAX; i++ )
    {
        if ( a[i] == 0 ) p[pn++] = i;
        for ( j = 0; j < pn && i * p[j] < MAX && (p[j] <= a[i] || a[i] == 0); j++ )
            a[i*p[j]] = p[j];
    }
}

void Euler_Farey ()
{
    for ( int i = 2; i < MAX; i++ )
    {
        if ( a[i] == 0 )
            eul[i] = i - 1;
        else
        {
            int k = i / a[i];
            if ( k % a[i] == 0 ) eul[i] = eul[k] * a[i];
            else eul[i] = eul[k] * ( a[i] - 1 );
        }
    }

    eul[1] = 1;
    for ( int i = 2; i < MAX; i++ )
        eul[i] += eul[i-1];
}

int main()
{
    int n, t;
    prime();
    Euler_Farey();
    scanf("%d",&t);
    for ( int i = 1; i <= t; i++ )
    {
        scanf("%d",&n);
        printf("%d %d %d\n",i,n,eul[n]*2+1);
    }
    return 0;
}


 

这是一道比较经典的计数问题。题目描述如下: 给定一个 $n \times n$ 的网格图,其中一些格子被标记为障碍。一个连通块是指一些被标记为障碍的格子的集合,满足这些格子在网格图中连通。一个格子是连通的当且仅当它与另一个被标记为障碍的格子在网格图中有公共边。 现在,你需要计算在这个网格图中,有多少个不同的连通块,满足这个连通块的大小(即包含的格子数)恰好为 $k$。 这是一道比较经典的计数问题,一般可以通过计算生成函数的方来解决。具体来说,我们可以定义一个生成函数 $F(x)$,其中 $[x^k]F(x)$ 表示大小为 $k$ 的连通块的个数。那么,我们可以考虑如何计算这个生成函数。 对于一个大小为 $k$ 的连通块,我们可以考虑它的形状。具体来说,我们可以考虑以该连通块的最左边、最上边的格子为起点,从上到下、从左到右遍历该连通块,把每个格子在该连通块中的相对位置记录下来。由于该连通块的大小为 $k$,因此这些相对位置一定是 $(x,y) \in [0,n-1]^2$ 中的 $k$ 个不同点。 现在,我们需要考虑如何计算这些点对应的连通块是否合。具体来说,我们可以考虑从左到右、从上到下依次处理这些点,对于每个点 $(x,y)$,我们需要考虑它是否能够与左边的点和上边的点连通。具体来说,如果 $(x-1,y)$ 和 $(x,y)$ 都在该连通块中且它们在网格图中有公共边,那么它们就是连通的;同样,如果 $(x,y-1)$ 和 $(x,y)$ 都在该连通块中且它们在网格图中有公共边,那么它们也是连通的。如果 $(x,y)$ 与左边和上边的点都不连通,那么说明这个点不属于该连通块。 考虑到每个点最多只有两个方向需要检查,因此时间复杂度为 $O(n^2 k)$。不过,我们可以使用类似于矩阵乘的思想,将这个过程优化到 $O(k^3)$ 的时间复杂度。 具体来说,我们可以设 $f_{i,j,k}$ 表示状态 $(i,j)$ 所代表的点在连通块中,且连通块的大小为 $k$ 的方案数。显然,对于一个合的 $(i,j,k)$,我们可以考虑 $(i-1,j,k-1)$ 和 $(i,j-1,k-1)$ 这两个状态,然后把点 $(i,j)$ 加入到它们所代表的连通块中。因此,我们可以设计一个 $O(k^3)$ 的 DP 状态转移,计算 $f_{i,j,k}$。 具体来说,我们可以考虑枚举连通块所包含的最右边和最下边的格子的坐标 $(x,y)$,然后计算 $f_{x,y,k}$。对于一个合的 $(x,y,k)$,我们可以考虑将 $(x,y)$ 所代表的点加入到 $(x-1,y,k-1)$ 和 $(x,y-1,k-1)$ 所代表的连通块中。不过,这里需要注意一个细节:如果 $(x-1,y)$ 和 $(x,y)$ 在网格图中没有相邻边,那么它们不能算作连通的。因此,我们需要特判这个情况。 最终,$f_{n,n,k}$ 就是大小为 $k$ 的连通块的个数,时间复杂度为 $O(n^2 k + k^3)$。 参考代码:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值