抄袭自 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)威尔逊定理
(2)欧拉定理
![](https://images2015.cnblogs.com/blog/894625/201603/894625-20160319151305724-1288391640.png)
![](https://images2015.cnblogs.com/blog/894625/201603/894625-20160319151324428-1999039791.png)
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 ",ans); 7 }
其充要条件为 (a-b)%mod n == 0
例:求解 ax=b(mod n)
因为由充要条件得 (ax-b) % n == 0 ---> ax-b = ny ----> ax-ny = b
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 ", 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 }
简便方法:φ(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; }
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 )
![](https://images2015.cnblogs.com/blog/894625/201603/894625-20160319161302256-121610105.png)
问题:一堆物品
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
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 }