ZOJ 3707 Calculate Prime S

ZOJ 3707 Calculate Prime S

题意:定义了S[n]为{1,2,...,n}的非连续数字的子集的个数,然后对于每个S[n],如果对于每个1<=i<n,gcd(S[i],S[n])=1,则称S[n]是素的S。对于第k个素的S,找到一个最小的S[n],使得S[n]是x的倍数,并且S[n]>=第k个素的S。然后输出(S[n]/x)%m。

首先证明几个结论。

I. gcd(ab,c)=gcd(a,c) 当gcd(b,c)=1时。
证明:gcd(ab,c)=gcd(a,c)*gcd(b,c)=gcd(a,c)

II. gcd(a,b)=gcd(b, a%b)。
证明:假设a>=b,则a=nb+m。假设g|a,g|b,所以g|(nb+m),所以g|m。因为a%b=(nb+m)%b=m,所以得证。

III. gcd(fib(i),fib(i+1))=1。
证明:因为fib(i+1)=fib(i)+fib(i-1),
所以gcd(fib(i),fib(i+1))=gcd(fib(i),fib(i)+fib(i-1))=gcd(fib(i),(fib(i)+fib(i-1))%fib(i))=gcd(fib(i),gcd(fib-1)),
可以把euclid的过程给展开,得到:
fib(i+1)=1*fib(i)+fib(i-1)
fib(i)=1*fib(i-1)+fib(i-2)
fib(i-1)=1*fib(i-2)+fib(i-3)
......
fib(4)=1*fib(3)+fib(2)
fib(3)=2*fib(2)+0
最后一行写出来就是:
2 = 2*1+0
所以gcd(fib(i),fib(i+1))=fib(2)=1;
英文原文证明以及反证法参考:
Consecutive Fibonacci Numbers Relatively Prime
http://mathforum.org/library/drmath/view/52716.html

IV. fib(u+v)=fib(u)fib(v-1)+fib(u+1)fib(v).
证明:利用反证法。
当u=1时:fib(1+v)=fib(1)fib(v-1)+fib(2)fib(v)=fib(v-1)+fib(v) 成立。
假设u=n时成立:fib(n+v)=fib(n)fib(v-1)+fib(n+1)fib(v) 成立。
u=n+1时:
fib(n+1+v)=fib(n+v)+fib(n+v-1)=fib(n)fib(v-1)+fib(n+1)fib(v) + fib(n-1)fib(v-1)+fib(n)fib(v)
=fib(v-1)(fib(n)+fib(n-1)) + fib(v)(fib(n-1)+fib(n)) = fib(n+1)fib(v-1)+fib(n+2)fib(v) = fib(n+1+v)
所以,由数学归纳法可以得证。
英文原文证明以及Binet Formula证明参考:
http://mathforum.org/library/drmath/view/51624.html

V. fibonacci-gcd proof: gcd(fib(n),fib(m))=fib(gcd(n,m)).
证明:假设n<=m,则: gcd(fib(n),fib(m))=gcd(fib(n),fib(n+k)),由证明IV,可得:
gcd(fib(n),fib(n+k))=gcd(fib(n),fib(n)fib(k-1)+fib(n+1)fib(k))
=gcd(fib(n),(fib(n)fib(k-1)+fib(n+1)fib(k))%fbi(b))
=gcd(fib(n),fib(n+1)fib(k)),由证明III,gcd(fib(b),fib(n+1))=1,所以由证明I可得:
gcd(fib(n),fib(n+1)fib(k))=gcd(fib(n),fib(k)),所以可得:
gcd(fib(n),fib(m))=gcd(fib(n),fib(m%n))。由展开euclid算法可知,算法在gcd(fib(g),fib(0))时停止,而g=gcd(m,n)。
所以由euclid算法的定义可得gcd(fib(g),fib(0))=fib(g)。
所以得证gcd(fib(n),fib(m))=fib(gcd(n,m))
英文原文证明参考:
http://mathforum.org/library/drmath/view/61690.html

VI. (a/b)%c=(a%bc)/b 当 b|a。
证明:因为b|a,所以a=kb。
左边=(a/b)%c=(kb/b)%c=k%c。
右边=(kb%bc)/b=((k%c)b)/b=k%c。
所以得证。

VII. fibonacci的一个结论:

矩阵快速幂即可。

VIII. 又一个结论,因为x很小(x<=100),所以能在题目给定的时间内枚举到所需的S[n]。


至此,此题需要的证明已齐,只需要素数筛法预处理出所有的位数,然后再暴力枚举即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using  namespace std;

const  int N = 15486042;
int MOD;
int prime[1001000], top;
bool isnot[20010000];
int ncase;
int k, x, m;
LL now, next;

struct Matrix
{
    LL ma[2][2];
    Matrix()
    {
        ma[0][0]=ma[1][1]=1;
        ma[0][1]=ma[1][0]=0;
    }
     void init()
    {
        ma[0][0]=ma[1][0]=ma[0][1]=1;
        ma[1][1]=0;
    }
    Matrix  operator * ( const Matrix &a)  const
    {
        Matrix res;
         for (  int i = 0 ; i < 2 ; i++ )
             for (  int j = 0 ; j < 2 ; j++ )
            {
                res.ma[i][j]=0;
                 for (  int k = 0 ; k < 2 ; k++ )
                    res.ma[i][j]=(res.ma[i][j]+ma[i][k]*a.ma[k][j])%MOD;
                res.ma[i][j]%=MOD;
            }
         return res;
    }
};

void init_prime()
{
    memset(isnot,0, sizeof(isnot));
    top = 1;
     for (  int i = 2 ; i < N ; i++ )
         if (!isnot[i])
        {
            prime[top++]=i;
             if (i<10000)
                 for (  int j = i+i ; j < N ; j+=i )
                    isnot[j]=1;
        }
    prime[1]=3;prime[2]=4;
}

void init( int k)
{
    Matrix res, a;
    a.init();
     while (k)
    {
         if (k&1)
            res = res*a;
        a=a*a;
        k>>=1;
    }
    next = (res.ma[0][0]+res.ma[1][0])%MOD;
    now = (res.ma[0][1]+res.ma[1][1])%MOD;
}

int main()
{
    init_prime();
    scanf("%d", &ncase);
     while (ncase--)
    {
        scanf("%d%d%d", &k, &x, &m);
        MOD = x*m;
        init(prime[k]);
         while (now%x)
        {
            swap(now,next);
            next = (now+next)%MOD;
        }
        printf("%lld\n", now/x);
    }
     return 0;
}

你可能感兴趣的:(ZOJ 3707 Calculate Prime S)