数论

抄袭自 http://www.cnblogs.com/linyujun/

1.错排原理:

  当考虑第n封信时,若前n-1封信恰有一封正确,  则第n封信的情况为 (n-1)*f(n-2);

          若前n-1封信已经全错排,则任取一封与第n封交换,情况为(n-1)*f(n-1);

  (n-1)*[(f-2)+(f-1)]  [n>2]

 

*2.组合数 ,O(n)求

    

#include<cstdio>
const int N = 200000 + 5;
const int MOD = (int)1e9 + 7;
int F[N], Finv[N], inv[N];//F是阶乘,Finv是逆元的阶乘 
void init(){
    inv[1] = 1;
    for(int i = 2; i < N; i ++){
        inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;  //求逆元
    }
    F[0] = Finv[0] = 1;
    for(int i = 1; i < N; i ++){
        F[i] = F[i-1] * 1ll * i % MOD;
        Finv[i] = Finv[i-1] * 1ll * inv[i] % MOD;
    }
}
int comb(int n, int m){//comb(n, m)就是C(n, m) 
    if(m < 0 || m > n) return 0;
    return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;
}
int main(){
    init();
}

 

  现在来了新问题,如果n和m很大呢,

  比如求C(n, m) % p  , n<=1e18,m<=1e18,p<=1e5

  卢卡斯定理

  C(n, m) % p  =  C(n / p, m / p) * C(n%p, m%p) % p

   1 LL Lucas(LL n, LL m, int p){

   2 return m ? Lucas(n/p, m/p, p) * comb(n%p, m%p, p) % p : 1;

   3 } 

3杂七杂八.

    acos的使用: hdu 2080,acos求到的是弧度   

      acos(res)*180/PI:

 

4.gcd  lcm: 

 1 /*
 2 12 8
 3 8  4
 4 4  0
 5 */
 6 ll gcd(ll a,ll b){    //假设 a > b
 7      return b == 0 ? a : gcd(b,a%b); 
 8 }
 9 ll lcm(ll a,ll b){
10     return a*b/gcd(a,b);
11 }

*5.扩展gcd:

 1 // ax+by = gcd(a, b) = d  已知a,b. 解 x,y,d; 
 2 // b = 0 时:  ax = gcd(a,0) = d;  --> d = a , x = 1 ,y = 0
 3 void ext_gcd(ll a,ll b,ll &x,ll &y,ll &d){
 4     if(!b){
 5         d = a, x = 1 , y = 0;        
 6     }else{
 7         ext_gcd(b,a%b,y,x,d);    // y,x交换位置
 8         y = y - x*(a/b);        // y = y - x*(a/b);
 9     }
10 }

&6.数论四大定理:转自  http://www.cnblogs.com/linyujun/p/5194142.html

(1)威尔逊定理

若p为质数,则p可整除(p-1)!+1.即  ((p-1)!+1)%p == 0 (p为质数)

(2)欧拉定理

欧拉定理:若a,n为正整数,且a,n互素,即gcd(a,n) = 1;
 
则a^φ(n) ≡ 1 (mod n):    即(a^φ(n))%n = 1;   
 
φ(n) 欧拉函数是求 1到n-1 中 与n互质的数 的数目
(3).孙子定理(中国剩余定理)
(4).费马小定理:
p是质数,
若p不能整除a,  则 a^(p-1) ≡1(mod p) 若 a%p!=0  则 (a^(p-1))%p == 1 
若p能整除a,     则 a^(p-1) ≡0(mod p) 若 a%p==0 则 (a^(p-1))%p == 0
 
7.模运算:
  
 
     (a  /  b) % p = (a * inv(a) ) % p = (a % p * inv(a) % p) % p
      (a / b)%p = (a*(a的逆元))%p
 
    
    大数取模
    
1 void BigNumberMod(const char str[],int m){
2     int ans = 0;
3     int len = strlen(str);
4     for(int i = 0 ; i < len ; i ++)
5         ans = (int)(((long long)ans*10+str[i]-'0')%m);
6     printf("%d\n",ans);
7 }

 

*8.线性模方程 和 逆元
    (1)a=b(mod n) a和b关于 n 同余, 即   a mod n == b mod n

    其充要条件为  (a-b)%mod n == 0

          例:求解 ax=b(mod n)

          因为由充要条件得 (ax-b) % n == 0 ---> ax-b = ny  ---->   ax-ny = b

    (2)ax=1(mod n)解x称为 a 关于模n 的逆,当gcd(a,n)=1时(即a,n互质),方程有唯一解,否则无解
       1.同上:  (ax-1)% n ==0   --->   ax-1 == ny  ---->  ax-ny == 1 == gcd(a,n);
        即 ext_gcd(a,n,x,y,d); 当 d != 1时,方程无解
         2. a^(n-2) ≡ inv(a) (mod n)
       3.
          当n是个质数的时候有
          inv(a) = (n - n / a) * inv(n % a) % n
  
 1 #include<cstdio>
 2 typedef long long LL;
 3 LL inv(LL t, LL p) {//求t关于p的逆元,注意:t要小于p,最好传参前先把t%p一下 
 4     return t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p;
 5 }
 6 int main(){
 7     LL a, p;
 8     while(~scanf("%lld%lld", &a, &p)){
 9         printf("%lld\n", inv(a%p, p));
10     }
11 }
 1 #include<cstdio>
 2 const int N = 200000 + 5;
 3 const int MOD = (int)1e9 + 7;
 4 int inv[N];
 5 int init(){
 6     inv[1] = 1;
 7     for(int i = 2; i < N; i ++){
 8         inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;
 9     }
10 }
11 int main(){
12     init();
13 }

 

 
9.欧拉函数的实现φ(x) 表示 1~x-1中与x互质的数   φ(12):与12互质的数有 3 5 7 11 ,其中φ(1)=1
    
    φ(12)->  12 = 2 * 2 * 3;
    12个数中 删去 2 4 6 8 10 12 3 6 9 12  + 回重复的(3,6两个数字)(容斥) = 4
    

    简便方法:φ(30)的计算方法就是先找30的质因数分别是2,3,5

      φ(30) = 30* 1/2 * 2/3 * 4/5就搞定了

    

//φ(30) = 30* 1/2 * 2/3 * 4/5就搞定了
//   30  = 2 * 3 * 5;
int phi(int x){
    int ans = x;
    for(int i = 2; i*i <= x; i++){
        if(x % i == 0){
            ans = ans / i * (i-1);
            while(x % i == 0) x /= i;
        }
    }
    if(x > 1) ans = ans / x * (x-1);  // x = 31时,1-30都与其互质
    return ans;
}
求n个数的欧拉函数:
 1 #include<cstdio>
 2 const int N = 100000 + 5;
 3 int phi[N];
 4 void Euler(){
 5     phi[1] = 1;
 6     for(int i = 2; i < N; i ++){
 7         if(!phi[i]){
 8             for(int j = i; j < N; j += i){
 9                 if(!phi[j]) phi[j] = j;
10                 phi[j] = phi[j] / i * (i-1);
11             }
12         }
13     }
14 }
15 int main(){
16     Euler();
17 }

有其它性质

  p为质数

  1. phi(p)=p-1   

  2. 如果i mod p = 0, 那么 phi(i * p)=phi(i) * p         

  3.若i mod p ≠0,  那么 phi( i * p )=phi(i) * ( p-1 )   

  另:在 a与p互质的情况下  
    
 
10.中国剩余定理:
    a*x  = 1 (mod p) :x是a关于p的逆元    x = inv(a,p);

    问题:一堆物品

        3个3个分剩2个(x%3==2)   5个5个分剩3个      7个7个分剩2个

        问这个物品有多少个

             构造                  *剩余的数

          5*7*inv(5*7,  3) % 3  =  1    -->  2*5*7*inv(5*7,  3) % 3  =  2*1

          3*7*inv(3*7,  5) % 5  =  1    -->  3*3*7*inv(3*7,  5) % 5  =  3*1

          3*5*inv(3*5,  7) % 7  =  1    --> 

        令   a = 2 * 5*7*inv(5*7,  3)     b = 3 * 3*7*inv(3*7,  5)     c = 2 * 3*5*inv(3*5,  7) 

        那么  a % 3 = 2            b % 5 = 3            c % 7 = 2

        其实答案就是a+b+c

 
11.康托展开:用最少空间保存所有状态
      康托展开的公式是 X=a0*(n-1)!+a1*(n-2)!+......
    编码
       其中,ai为从i开始到最后是排在第几个大的(从0开始)。
      X(1,2,3,4): a0 = 0  a1 = 0  a2 = 0  a3 = 0    X = 0
      X(3,4,1,2): a0 = 2  a1 = 2  a2 = 1  a3 = 0    X = 17 = 2*3! + 2*2!+1*1
     解码  
      17% 3!= 2....5    //1.2.3.4 有4位  老二为3
      5%2!  = 2 ....1    //1.2.4      老二为4 
      1%1!  = 1 ....0    //....
             0        //....
 练习:hdu1430,hdu1043 
 1 void cantor(int s[], LL num, int k){//康托展开,把一个数字num展开成一个数组s,k是数组长度
 2     int t;
 3     bool h[k];//0到k-1,表示是否出现过 
 4     memset(h, 0, sizeof(h)); 
 5     for(int i = 0; i < k; i ++){
 6         t = num / fac[k-i-1];
 7         num = num % fac[k-i-1];
 8         for(int j = 0, pos = 0; ; j ++, pos ++){
 9             if(h[pos]) j --;
10             if(j == t){
11                 h[pos] = true;
12                 s[i] = pos + 1;
13                 break;
14             }
15         }
16     }
17 }
18 void inv_cantor(int s[], LL &num, int k){//康托逆展开,把一个数组s换算成一个数字num 
19     int cnt;
20     num = 0;
21     for(int i = 0; i < k; i ++){
22         cnt = 0;
23         for(int j = i + 1; j < k; j ++){
24             if(s[i] > s[j]) cnt ++;//判断几个数小于它
25         }
26         num += fac[k-i-1] * cnt;
27     }
28 }

 

12.
 
13.
 
14.
 
15.
 
16.
 
17.
 
18.
 
19.
 
20.
 
 
 
 
posted @ 2016-03-17 15:13  zstu_jack  阅读(199)  评论(0)    收藏  举报