zoukankan      html  css  js  c++  java
  • 暑假训练数论

    斐波那契  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:
    ",++ca);
            printf("%I64d
    ",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;
    }
  • 相关阅读:
    <转>lua 调用 C函数
    <转>VC之获取CPU序列号
    <转> 求结构体偏移
    借助 FireBug 进行轻量级代码自动生成。
    内存盘配置IIS临时目录
    导出带有复合表头的Excel方案.
    左右互博之精简代码
    [转]JavaScript 秘密花园
    MVC中,Ajax Post 数组的实现方案
    Sql 存储过程 参数传递空字符串, 到SQL 端变成了 空格!!
  • 原文地址:https://www.cnblogs.com/wpbing/p/9438679.html
Copyright © 2011-2022 走看看