暑假训练数论

斐波那契  https://www.cnblogs.com/Milkor/p/4734763.html

通项公式

求证过程就算了

斐波那契和矩阵的关系:

实现过程是根据快速幂改编的

void fen(ll r[][2],ll k[][2])//矩阵乘法
{
    ll ans[2][2];
    ans[0][0]=(r[0][0]*k[0][0]%mod+r[0][1]*k[1][0]%mod)%mod;
    ans[0][1]=(r[0][0]*k[0][1]%mod+r[0][1]*k[1][1]%mod)%mod;
    ans[1][0]=(r[1][0]*k[0][0]%mod+r[1][1]*k[1][0]%mod)%mod;
    ans[1][1]=(r[1][0]*k[0][1]%mod+r[1][1]*k[1][1]%mod)%mod;
    r[0][0]=ans[0][0],r[0][1]=ans[0][1],r[1][0]=ans[1][0],r[1][1]=ans[1][1];
}
ll fbnq(ll num)
{
    if(num==0) return 0;
    
    ll k[2][2]={1,0,0,1};
    ll r[2][2]={1,1,1,0};
    while(num>1)
    {
        if(((num&1)!=0)) fen(k,r);
        
        fen(r,r);//思想可以参考快速幂 
        
        num>>=1;
    }
    fen(r,k);
    return r[0][1];
}

 

一些常用性质

例题

2007年到来了。经过2006年一年的修炼,数学神童zouyu终于把0到100000000的Fibonacci数列 
(f[0]=0,f[1]=1;f[i] = f[i-1]+f[i-2](i>=2))的值全部给背了下来。 
接下来,CodeStar决定要考考他,于是每问他一个数字,他就要把答案说出来,不过有的数字太长了。所以规定超过4位的只要说出前4位就可以了,可是CodeStar自己又记不住。于是他决定编写一个程序来测验zouyu说的是否正确。

Input输入若干数字n(0 <= n <= 100000000),每个数字一行。读到文件尾。Output输出f[n]的前4个数字(若不足4个数字,就全部输出)。Sample Input

0
1
2
3
4
5
35
36
37
38
39
40

Sample Output

0
1
1
2
3
5
9227
1493
2415
3908
6324
1023

 数字有点大,递推不行,可以运用公式,但要求前4位,可以

log10(10234432)=log10(1.0234432*10^7)=log10(1.0234432)+7;

log10(1.0234432)就是log10(10234432)的小数部分.

log10(1.0234432)=0.010063744
10^0.010063744=1.023443198

#include<iostream>
#include<cmath>
#include<string.h>
using namespace std;
int f[20];
int main()
{
    int n;
    double s;
    f[0]=0,f[1]=1;
    for(int i=2;i<20;i++)
    f[i]=f[i-1]+f[i-2];
    
    while(cin>>n)
    {
        if(n<20)
        {
            cout<<f[n]<<endl;
            continue;
        }
        s=log10(1.0/sqrt(5.0))+n*log10((1+sqrt(5.0))/2);
        s=s-(int)s;
        s=pow(10,s);
        while(s<1000) s*=10;
        cout<<(int)s<<endl;
    }
}
We all know the definition of Fibonacci series: fib[i]=fib[i-1]+fib[i-2],fib[1]=1,fib[2]=1.And we define another series P associated with the Fibonacci series: P[i]=fib[4*i-1].Now we will give several queries about P:give two integers L,R, and calculate ∑P[i](L <= i <= R).

InputThere is only one test case. 
The first line contains single integer Q – the number of queries. (Q<=10^4) 
Each line next will contain two integer L, R. (1<=L<=R<=10^12)OutputFor each query output one line. 
Due to the final answer would be so large, please output the answer mod 1000000007.Sample Input

2
1 300
2 400

Sample Output

838985007
352105429
题意:sum[i]=fib[4*i-1] 给出L,R,求出中间的p[i]的和。

Sum[i]=Fib[3]+Fib[7]+......+Fib[4i-1],
因为Fib[m+n-1]=Fib[m-1]*Fib[n-1]+Fib[m]*Fib[n]
所以Fib[4i-1]=Fib[2i+2i-1]=Fib[2i-1]*Fib[2i-1]+Fib[2i]*Fib[2i]
所以Sum[i]=Fib[1]*Fib[1]+Fib[2]*Fib[2]+......+Fib[2i]*Fib[2i]
又Fib[i]*Fib[i+1]+Fib[i+1]*Fib[i+1]=Fib[i+1]*Fib[i+2]
代入计算可得Sum[i]=Fib[2i]*Fib[2i+1]-Fib[0]*Fib[1]=Fib[2i]*Fib[2i+1]
最后发现Sum[i]只和fibonacci的两项有关。
关于fibonacci数列的一项可以构造矩阵在O(logn)时间内算出。
#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
#define mod 1000000007
#define ll long long
void fen(ll r[][2],ll k[][2])
{
    ll ans[2][2];
    ans[0][0]=(r[0][0]*k[0][0]%mod+r[0][1]*k[1][0]%mod)%mod;
    ans[0][1]=(r[0][0]*k[0][1]%mod+r[0][1]*k[1][1]%mod)%mod;
    ans[1][0]=(r[1][0]*k[0][0]%mod+r[1][1]*k[1][0]%mod)%mod;
    ans[1][1]=(r[1][0]*k[0][1]%mod+r[1][1]*k[1][1]%mod)%mod;
    r[0][0]=ans[0][0],r[0][1]=ans[0][1],r[1][0]=ans[1][0],r[1][1]=ans[1][1];
}
ll fbnq(ll num)
{
    if(num==0) return 0;
    
    ll k[2][2]={1,0,0,1};
    ll r[2][2]={1,1,1,0};
    while(num>1)
    {
        if(((num&1)!=0)) fen(k,r);
        
        fen(r,r);
        
        num>>=1;
    }
    fen(r,k);
    return r[0][1];
}
int main()    
{
    int t;
    cin>>t;
    while(t--)
    {
        ll l,r;
        ll sl=0,sr=0;
        cin>>l>>r;
        l--;
        sl=fbnq(2*l)*fbnq(2*l+1);
        sr=fbnq(2*r)*fbnq(2*r+1);
        cout<<((sr-sl)%mod+mod)%mod<<endl;
    }
}

卡特兰数

h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)*h(0) (n>=2)
例如:h(2)=h(0)*h(1)+h(1)*h(0)=1*1+1*1=2
h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=1*2+1*1+2*1=5
 
h(n)=h(n-1)*(4*n-2)/(n+1);
递推关系的解为:
h(n)=C(2n,n)/(n+1) (n=0,1,2,...)
递推关系的另类解为:
h(n)=c(2n,n)-c(2n,n-1)(n=0,1,2,...)
应用
 1、出栈次序:一个栈(无穷大)的进栈次序为1、2、3……n。不同的出栈次序有几种。(边进边出)
对于出栈序列中的每一个数字,在它后面的、比它小的所有数字,一定是按递减顺序排列的。

出栈顺序:3 2 4 1 也是合法的,对于数字 3 而言,它后面比 3 小的数字有: 2 1,这个顺序是递减的;对于数字 2 而言,它后面的比它 小的数字只有 1,也算符合递减顺序;对于数字 4 而言,它后面的比它小的数字也只有1,因此也符合递减顺序。

出栈顺序:3 1 4 2 是不合法的,因为对于数字 3 而言,在3后面的比3小的数字有:1 2,这个顺序是一个递增的顺序(1-->2)。

            我们可以这样想,假设k是最后一个出栈的数。比k早进栈且早出栈的有k-1个数,一共有h(k-1)种方案。比k晚进栈且早出栈的有n-k个数,一共有h(n-k)种方案。所以一共有h(k-1)*h(n-k)种方案。显而易见,k取不同值时,产生的出栈序列是相互独立的,所以结果可以累加。k的取值范围为1至n,所以结果就为h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0)。
            出栈入栈问题有许多的变种,比如n个人拿5元、n个人拿10元买物品,物品5元,老板没零钱。问有几种排队方式。熟悉栈的同学很容易就能把这个问题转换为栈。值得注意的是,由于每个拿5元的人排队的次序不是固定的,所以最后求得的答案要*n!。拿10元的人同理,所以还要*n!。所以这种变种的最后答案为h(n)*n!*n!。
 
 
2、凸多边形的三角形划分。一个凸的n边形,用直线连接他的两个顶点使之分成多个三角形,每条直线不能相交,问一共有多少种划分方案。
             这也是非常经典的一道题。我们可以这样来看,选择一个基边,显然这是多边形划分完之后某个三角形的一条边。图中我们假设基边是p1pn,我们就可以用p1、pn和另外一个点假设为pi做一个三角形,并将多边形分成三部分,除了中间的三角形之外,一边是i边形,另一边是n-i+1边形。i的取值范围是2到n-1。所以本题的解c(n)=c(2)*c(n-1)+c(3)*c(n-2)+...c(n-1)*c(2)。令t(i)=c(i+2)。则t(i)=t(0)*t(i-1)+t(1)*t(i-2)...+t(i-1)*t(0)。很明显,这就是一个卡特兰数了。
 
3、二叉树构成问题。有n个结点,问总共能构成几种不同的二叉树。
            我们可以假设,如果采用中序遍历的话,根结点第k个被访问到,则根结点的左子树有k-1个点、根结点的右指数有n-k个点。k的取值范围为1到n。讲到这里就很明显看得出是卡特兰数了。这道题出现在2015年腾讯实习生的在线笔试题中。有参加过的同学想必都有印象。
 
例题
 
度度熊最近很喜欢玩游戏。这一天他在纸上画了一个2行N列的长方形格子。他想把1到2N这些数依次放进去,但是为了使格子看起来优美,他想找到使每行每列都递增的方案。不过画了很久,他发现方案数实在是太多了。度度熊想知道,有多少种放数字的方法能满足上面的条件?
Input  第一行为数据组数T(1<=T<=100000)。 
  然后T行,每行为一个数N(1<=N<=1000000)表示长方形的大小。Output  对于每组数据,输出符合题意的方案数。由于数字可能非常大,你只需要把最后的结果对1000000007取模即可。Sample Input
2
1
3
Sample Output
Case #1:
1
Case #2:
5

数字应该是从小到大放置的,1肯定在左上角,所以1入栈,这时候我们放2,如果我们把2放在了1的下面就代表了1出栈,把2放在上面就代表了2也进栈(可以看一下hint中第二组样例提示),以此类推,这样去放数,正好就对应了上面一行入栈,下面一行出栈的情况,一共n行,对应上限为n的卡特兰数。

 

还有种想法

我们假设0代表这个数放在第一排,1代表这个数放在第二排,如果序列00001111

         就是  1 2 3 4

                  5 6 7 8 

         序列01010101  就是 1 3 5 7

                                        2 4 6 8

          这个问题就转化成有几种满足题目条件的01序列,通过观察发现每个位置之前0的个数大于等于1的个数

          如果把0看成入栈,1看成出栈,

          这个问题变转化成n个数的入栈的出栈次序的种类数,也就是卡特兰数


h(n)=h(n-1)*(4*n-2)/(n+1);这里运用这个公式,但是要注意除法没有同余  要用逆元。

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 1000010
#define mod 1000000007
#define LL long long
LL ktl[maxn],x,y;
LL exgcd(LL a,LL b)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    LL gcd = exgcd(b,a%b);
    LL tmp;
    tmp = x;
    x = y;
    y = tmp - a/b * y;
    return gcd;
}
LL yiyuan(int n)
{
    LL gcd = exgcd(n,mod);
    if(gcd == 1)
    return (x%mod + mod) % mod;
}
void init()
{
    memset(ktl,0,sizeof(ktl));
    ktl[1] = 1;
    for(int i = 2; i <= maxn-10; i++)
    {
        ktl[i] = (ktl[i-1]*(4*i-2)%mod * yiyuan(i+1)) % mod;
    }
}
int main()
{
    int t,n,ca = 0;
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        printf("Case #%d:\n",++ca);
        printf("%I64d\n",ktl[n]);
    }
    return 0;
}

 欧拉函数

转https://www.cnblogs.com/handsomecui/p/4755455.html

在数论中,对于正整数N,少于或等于N ([1,N]),且与N互质的正整数(包括1)的个数,记作φ(n)。

     φ函数的值:

    φ(x)=x(1-1/p(1))(1-1/p(2))(1-1/p(3))(1-1/p(4))…..(1-1/p(n)) 其中p(1),p(2)…p(n)为x

的所有质因数;x是正整数; φ(1)=1(唯一和1互质的数,且小于等于1)。注意:每种质因数只有一个。

     例如:

         φ(10)=10×(1-1/2)×(1-1/5)=4;

         1 3 7 9

         φ(30)=30×(1-1/2)×(1-1/3)×(1-1/5)=8;

         φ(49)=49×(1-1/7)=42;

欧拉函数的性质:

(1)   p^k型欧拉函数:

若N是质数p(即N=p), φ(n)= φ(p)=p-p^(k-1)=p-1。

若N是质数p的k次幂(即N=p^k),φ(n)=p^k-p^(k-1)=(p-1)p^(k-1)。

(2)mn型欧拉函数

设n为正整数,以φ(n)表示不超过n且与n互素的正整数的个数,称为n的欧拉函数值。若m,n互质,φ(mn)=(m-1)(n-1)=φ(m)φ(n)。

(3)特殊性质:

若n为奇数时,φ(2n)=φ(n)。

对于任何两个互质 的正整数a,n(n>2)有:a^φ(n)=1 mod n (恒等于)此公式即 欧拉定理

当n=p 且 a与素数p互质(即:gcd(a,p)=1)则上式有: a^(p-1)=1 mod n (恒等于)此公式即 费马小定理

 

同余定理:

     如果 a mod b = c 则有(a+kb) mod b =c(k为非0整数)

     如果 a mod b = c 则有(ka) mod b =kc (k为正整数)

     (a+b) mod c =((a mod c)+(b mod c )) mod c;

     (a*b) mod c=((a mod c)*(b mod c)) mod c

 

欧拉函数模板

int phi(int n)//直接求 
{
    int t=n;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            t=t/i*(i-1);
            
            while(n%i==0) n/=i;
        }
    }
    if(n>1) t=t/n*(n-1);
    return t;
}
int p[1000001];
void init()//打表 
{
    memset(p,0,sizeof(p));
    p[1]=1;
    for(int i=2;i<1000000;i++)
    {
        if(p[i]==0)
        {
            for(int j=i;j<1000000;j+=i)
            {
                if(p[j]==0)
                p[j]=j;
                
                p[j]=p[j]/i*(i-1);
            }
        }
    }
}

例题

POJ 2480

For each N, output ,∑gcd(i, N) 1<=i <=N, a line

#include<iostream>
#include<stdio.h>
#define ll long long
using namespace std;
ll phi(int n)
{
    ll res=n;
    for(ll i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            res=res/i*(i-1);
            while(n%i==0)n/=i;
        }
    }
    if(n>1) res=res/n*(n-1);
    return res;
 } 
 int main()
{
    ll n;
    while(scanf("%lld",&n)!=EOF)
    {
        ll ans=0;
        for(ll i=1;i*i<=n;i++)
        {
            if(n%i==0)
            {
                ans+=i*phi(n/i);//phi(n/i)则为和n最大公约数为i的个数 
                if(i*i!=n)
                {
                    ans+=n/i*phi(i);
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

快速乘法和快速幂

ll multi(ll a,ll b)
{
    ll res=0;
    while(b)
    {
        if(b&1) res=(res+a)%p;
        
        a=(a+a)%p;
        b>>=1;
    }
    return res;
}
ll powmod(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1) res=multi(res,a)%p;
        
        a=multi(a,a)%p;
        b>>=1;
    }
    return res;
}

 扩展欧几里德模板

ll ex_gcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll r=ex_gcd(b,a%b,y,x);
    y-=x*(a/b); 
    return r;
 } 

逆元模板

ll ex_gcd(ll a,ll b,ll &x,ll &y)
{
    if(a==0&&b==0) return -1;//无最大公约数
    if(b==0){
        x=1;
        y=0;
        return a;
    } 
    ll d=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
ll niyuan(ll a,ll n)
{
    ll x,y;
    ll d=ex_gcd(a,n,x,y);
    if(d==1) return (x%n+n)%n;
    else return -1;
}

 

posted @ 2018-08-08 08:27  踩在浪花上  阅读(509)  评论(0编辑  收藏  举报