NOIP 数学
Day 1
mod
(a+b)%c=(a%c+b%c)%c
(a-b)%c=(a%c-b%c)%c=((a-b)%c+c)%c
(a*b)%c=(a%c*b%c)%c=(1ll*a*b)%c//1ll=long long 类型的1
//求证((a%c+b%c)%c)=(a + b)%c:
a=x*c+x',b=y*c+y'
a%c=x',b%c=y'
(a+b)%c=(x*c+x'+y*c+y')%c=(x'+y')%c
a%c+b%c=x'+y'
((a%c+b%c)%c)=(x'+y')%c
((a%c+b%c)%c)=(a+b)%c
gcd&&lcm
注意:gcd(a,0) = a
int gcd(int a,int b){
if (b==0) return a;
return gcd(b,a%b);
}//最大公因数
lcm(a,b)=a/gcd(a,b)*b//最小公倍数
快速幂&&快速乘
有的时候O(n)也过不了,啊啊啊啊
int ksm(int x,int y,int p){//计算x^y%p
if(y==0)return 1;
int z=ksm(x,y/2,p);
//int z=ksm(x,y>>1,p);
z=1ll*z*z%p;
if(y%2==1) z=1ll*z*x%p;
//if(y&1) z=1ll*z*x%p;
return z;
}
int ksc(int x,int y,int p){//计算x*y%p
if(y==0)return 0;
int z=ksc(x,y/2,p);
//int z=ksm(x,y>>1,p);
z=(z+z)%p;
if (y%2==1)z=(z+x)%p;
//if(y&1) z=(z+x)%p;
return z;
}
矩阵
对矩阵进行加法的重载运算符:
inline Matrix operator+(const Matrix &a,const Matrix &b){//矩阵加法
Matrix res=Matrix(a.n,a.m);
rep(i,0,a.n-1,1) rep(j,0,a.m-1,1)
res.val[i][j]=(a.val[i][j]+b.val[i][j])%MOD;
return res;
}
对矩阵进行减法的重载运算符:
inline Matrix operator-(const Matrix &a,const Matrix &b){//矩阵减法
Matrix res=Matrix(a.n,a.m);
rep(i,0,a.n-1,1) rep(j,0,a.m-1,1)
res.val[i][j]=(a.val[i][j]-b.val[i][j])%MOD;
return res;
}
对矩阵进行乘法的重载运算符:
matrix operator*(const matrix &m1,const matrix &m2){//重载运算符,对*进行重新定义,只针对矩阵*矩阵,普通乘法不影响
matrix m3;
m3.n=m1.n;m3.m=m2.m;
for(int i=1;i<=m3.n;i++)
for(int j=1;j<=m3.m;j++)
for(int k=1;k<=m1.m;k++)
m3.z[i][j]+=m1.z[i][k]*m2.z[k][j];
return m3;
}
矩阵快速幂:
matrix ksm(matrix x,int y){//计算x矩阵的y次方
if(y==1) return x;
matrix z=ksm(x,y/2);
z=z*z;
if(y%2==1) z=z*x;
return z;
}
【补充:定义矩阵:
struct matrix{//定义一个矩阵
int n,m;
int z[10][10];
matrix(){
n=m=0;
memset(z,0,sizeof(z));
}
};
】
分解质因数
void factorize(int x){//对x分解质因数
int cnt;
for (int i=2;i*i<=x;i++)
if (x%i==0){
cnt++;
prime[cnt]=a;
while(x%i==0){
num[cnt]++;
x/=a;
}
}
if (x!=1){
cnt++;
prime[cnt]=x;
num[cnt]=1;
}
}
Day 2
exgcd
/*
题目:给定一个a,b和g(g=gcd(a,b))
求出两个数x,y,使得xa+yb=g,求出任意一组x,y的解即可
gcd(a,b)=gcd(b,a%b)=g
-----xa+yb=g
| x'b+y'(a%b)=g
| x'b+y'(a-b*(a/b))=g
| x'b+y'a-y'b*(a/b)=g
-----y'a+(x'-y'*(a/b))*b=g
∴x=y' y=(x'-y'*(a/b))
*/
int exgcd(int a,int b,int &x,int &y){//扩展欧几里得算法
//求g=gcd(a,b)
//以及xa+yb=g
if(b==0){
x=1,y=0;
return a;
}
int xp,yp;
int g=exgcd(b,a%b,xp,yp);
//xp*b+yp*(a%b)=g
//xp*b+yp*(a-b*(a/b))=g
//xp*b+yp*a-yp*b*(a/b)=g
//yp*a+(xp-yp*(a/b))*b=g
x=yp;
y=xp-yp*(a/b);
return g;
}
miller_rabin
#include<bits/stdc++.h>
using namespace std;
int ksm(int x,int y,int p)
//计算x^y%p
{
if (y==0) return 1;
int z=ksm(x,y/2,p);
z = 1ll * z * z % p;
if (y%2==1) z = 1ll * z * x % p;
return z;
}
/*
如果n为质数,n-1=d*2^r
例:n=37则d=9,r=2;
则至少成立一下一条性质:
1.a^d%n=1
2.存在0<=i<r a^(d*2^i)%n=n-1
[如果n不是质数也有可能成立]
*/
bool miller_rabin(int n,int a){
int d=n-1;
int r=0;
while(d%2==0)
d=d/2,r++;
int x=ksm(a,d,n);
if(x==1)return true;
for(int i=0;i<r;i++){
if(x==n-1)return true;
x=1ll*x*x%n;
}
return false;
}
int prime_list[]={2,3,5,7,13,23,37,73};
bool is_prime(int n){
if(n<2)return false;
for(int i=0;i<8;i++){
if(n==prime_list[i])return true;
if(n%prime_list[i]==0)return false;
if(!miller_rabin(n,prime_list[i]%n))return false;
}
return true;
}
欧拉定理&&欧拉函数&&费马小定理
#include<bits/stdc++.h>
using namespace std;
int get_phi(int n){
int phi=n;
for(int i=2;i*i<=n;i++)
if(n%i==0){
phi=phi/i*(i-1);
while(n%i==0)
n=n/i;
}
if(n!=1)phi=phi/n*(n-1);
return phi;//phi代表1~n中与n互质的数的个数
}
int ksm(int x,int y,int p)
//计算x^y%p
{
if (y==0) return 1;
int z=ksm(x,y/2,p);
z = 1ll * z * z % p;
if (y%2==1) z = 1ll * z * x % p;
return z;
}
long long n,p;
long long fac[1005],ifac[1005],inv[1005];
int main(){
/*
加法:(3+4)%5=2
减法:(3-4)%5:C++里为-1,而数学中为4
乘法:(3*4)%5=2
除法:(3/4)%5=?
逆元:
原式:a/b%P
找到一个c使a*c%P=a/b%P
把c称为b的逆元
费马小定理:
a^(P-1)≡1 (mod P)
条件:1.P是质数 2.gcd(a,P)=1
例:P=5,a=4
证明:a^(P-1)%P=4^4%5=256%5=1
等式两边同乘1/a,得a^(P-2)≡1/a(mod P)
代入原式:3/4%5
=3*(1/4)%5
=3*4^(5-2)%5
=2
公式:a/b%P---->1ll*a*ksm(b,P-2,P)%P
如果P不是质数呢?
例:5/8%9
那么我们就需要一个新的定理——欧拉定理:
a^(φ(P))≡1(mod P)
条件:gcd(a,P)=1
φ(P)欧拉函数,代表1~P中有多少个数和P互质
例:φ(4)=2 [1,3] φ(6) [1,5]
转化:a^(φ(P)-1)≡1/a(mod P)
代入原式:5/8%9
=5*8^(6-1)%9
公式:a/b%P--->1ll*a*ksm(b,φ(P)-1,P)%P
φ(n)怎么算呢?
如果暴力的话时间复杂度是O(n logn)
设P1是n的唯一一个质因子
1.当n=P1时 φ(n)=P1-1
2.当n=P1^2时 φ(n)=P1^2-P1=P1(P1-1)
3.当n=P1^k1时 φ(n)=(P1-1)/P1*n=n*(1-1/P1)
如果有多个质因子
两个因子:n=P1*P2 φ(n)=n-n/P1-n/P2+n/P1*P2
=n*(1-1/P1)*(1-1/P2)
t个因子:n=P1^k1*P2^k2*……Pt^kt
所以:φ(n)=n*
例题:求1~n中的每个数%p的逆元
1 2 3 …… n
1^(P-2) 2^(P-2) 3^(P-2) …… n^(P-2)
*/
//第一种方法:
cin>>n>>p;
//fac[i]=i!
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=1ll*fac[i-1]*i%p;//算阶乘
//ifac[i]=1/i! i!的逆元
ifac[n]=ksm(fac[n],p-2,p);
for(int i=n-1;i>=0;i--)
ifac[i]=1ll*ifac[i+1]*(i+1)%p;//算阶乘的逆元
//inv[i]=1/i i的逆元
//1/v=(i-1)!/i!
for(int i=1;i<=n;i++)
inv[i]=1ll*fac[i-1]*ifac[i]%p;//求每个数的逆元
for(int i=1;i<=n;i++){
cout<<inv[i]<<endl;
}
}
例题1:
求φ(1)~φ(n)每个数的值
phi[1]=1;
for(int i=2;i<=n;i++){
if(not_prime[i]==false){
cnt++;
prime[cnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=cnt;j++){
int x=prime[j]*i;//筛掉第j个质数的i倍
if(x>n)break;
not_prime[x]=true;
if(i%prime[j]==0){
phi[x]=prime[j]*phi[i];//积性函数的性质
break;
//每个数只被最小的质因子筛掉
}
phi[x]=phi[prime[j]]*phi[i];//线性筛的性质
}
//线性筛可以求积性函数
例题2: 求每个数的逆元
First way:
#include<bits/stdc++.h>
using namespace std;
int ksm(int x,int y,int p)
//计算x^y%p
{
if (y==0) return 1;
int z=ksm(x,y/2,p);
z = 1ll * z * z % p;
if (y%2==1) z = 1ll * z * x % p;
return z;
}
long long n,p;
long long fac[1005],ifac[1005],inv[1005];
int main(){
/*例题:求1~n中的每个数%p的逆元
1 2 3 …… n
1^(P-2) 2^(P-2) 3^(P-2) …… n^(P-2)
*/
//第一种方法:
cin>>n>>p;
//fac[i]=i!
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=1ll*fac[i-1]*i%p;//算阶乘
//ifac[i]=1/i! i!的逆元
ifac[n]=ksm(fac[n],p-2,p);
for(int i=n-1;i>=0;i--)
ifac[i]=1ll*ifac[i+1]*(i+1)%p;//算阶乘的逆元
//inv[i]=1/i i的逆元
//1/v=(i-1)!/i!
for(int i=1;i<=n;i++)
inv[i]=1ll*fac[i-1]*ifac[i]%p;//求每个数的逆元
for(int i=1;i<=n;i++){
cout<<inv[i]<<endl;
}
return 0;
}
Second way:
#include<bits/stdc++.h>
using namespace std;
void solve(int p1,int a1,int p2,int a2,int &p,int &a){
//x%p1=a1
//x%p2=a2
//x%p=a
if(p1<p2)swap(p1,p2),swap(a1,a2);
int x=a1;
int g=__gcd(p1,p2);
int l=p1/g*p2;
while(x<=l&&x%p2!=a2)
x+=p1;
if(x>l)p=-1,a=-1;
else p=l,a=x;
}
int main(){
int n,p1,p2,a1,a2,p,a=1,x;
cin>>n;
cin>>p1>>a1;
for(int i=2;i<=n;i++){
cin>>p2>>a2;
solve(p1,a1,p2,a2,p,a);
p1=p2,a1=a2;
}
cout<<a<<endl;
}
同余方程
/*
题目:给定一个a,b和g(g=gcd(a,b))
求出两个数x,y,使得xa+yb=g,求出任意一组x,y的解即可
gcd(a,b)=gcd(b,a%b)=g
-----xa+yb=g
| x'b+y'(a%b)=g
| x'b+y'(a-b*(a/b))=g
| x'b+y'a-y'b*(a/b)=g
-----y'a+(x'-y'*(a/b))*b=g
∴x=y' y=(x'-y'*(a/b))
*/
#include<bits/stdc++.h>
using namespace std;
int exgcd(int a,int b,int &x,int &y){
//求g=gcd(a,b)
//以及xa+yb=g
if(b==0){
x=1,y=0;
return a;
}
int xp,yp;
int g=exgcd(b,a%b,xp,yp);
//xp*b+yp*(a%b)=g
//xp*b+yp*(a-b*(a/b))=g
//xp*b+yp*a-yp*b*(a/b)=g
//yp*a+(xp-yp*(a/b))*b=g
x=yp;
y=xp-yp*(a/b);
return g;
}
int main(){
int x,y,a,b;
cin>>a>>b;
y=1;
exgcd(a,b,x,y);
if(x%b>=0)cout<<x%b<<endl;
else cout<<(x%b+b)%b<<endl;
}
质数筛法
//判断质数
//第一种做法 O(n log n):
for(int i=1;i<=n;i++)
for(int j=i+i;j<=n;j+=i)
not_primne[b]=true;
for(int i=2;i<=n;i++)
if(not_prime[a]==false)prime[++cnt]=a;
//第二种做法 O(n log(log n))≈O(n):
//只需枚举质数的倍数:
for(int i=1;i<=n;i++)
if(not_prime[a]==false)
for(int j=i+i;j<=n;j+=i)
not_primne[b]=true;
//第三种做法 O(n)
//线性筛:只在每个数的最小质因子的位置筛掉该数
for(int i=2;i<=n;i++){
if(not_prime[i]==false){
cnt++;
prime[cnt]=i;
}
for(int j=1;j<=cnt;j++){
int x=prime[j]*i;//筛掉第j个质数的i倍
if(x>n)break;
not_prime[x]=true;
if(i%prime[j]==0)break;//每个数只被最小的质因子筛掉
}
/* 积性函数:
有一个函数为f,当gcd(a,b)1(即a,b互质) f(a)*f(b)=f(a*b)
φ就是积性函数
φ(nm)=nm*(1-1/p1)*(1-1/p2)*……*(1-1/pt)*(1-1/q1)*(1-1/q2)*……*(1-1/qw)
完全积性函数(积性函数除去互质的条件):
*/
中国剩余定理
扩展欧几里得算法:
/*
---x%p1=a1
|
---x%p2=a2
x=k1*p1+a1=k2*p2+a2
k1*p1-k2*p2=a2-a1
扩展欧几里得算法:xa+yb=g
*/
#include<bits/stdc++.h>
using namespace std;
int exgcd(int a,int b,int &x,int &y){//扩展欧几里得算法
//求g=gcd(a,b)
//以及xa+yb=g
if(b==0){
x=1,y=0;
return a;
}
int xp,yp;
int g=exgcd(b,a%b,xp,yp);
//xp*b+yp*(a%b)=g
//xp*b+yp*(a-b*(a/b))=g
//xp*b+yp*a-yp*b*(a/b)=g
//yp*a+(xp-yp*(a/b))*b=g
x=yp;
y=xp-yp*(a/b);
return g;
}
void solve(int p1,int a1,int p2,int a2,int &p,int &a){
//x%p1=a1
//x%p2=a2
//x%p=a
//x=k1*p1+a1=k2*p2+a2
//k1*p1-k2*p2=a2-a1
int g,k1,k2;
g=exgcd(p1,p2,k1,k2);
//k1*p1+k2*p2=g
k2=-k2;
//k1*p1-k2*p2=g
//算一下a2-a1是g的多少倍
if((a2-a1)%g!=0)p=-1,a=-1;
else {
int k=(a2-a1)/g;
k1=k1*k;
k2=k2*k;
//k1*p1-k2*p2=a2-a1;
int x=k1*p1+a1;
p=p1/g*g;
a=(x%p+p)%p;
}
}
int main(){
int p,p1,p2,a,a1,a2;
int n;
cin>>n;
cin>>p1>>a1;
for(int i=2;i<=n;i++){
cin>>p2>>a2;
solve(p1,a1,p2,a2,p,a);
p1=p2,a1=a2;
}
cout<<a<<endl;
}
大数翻倍法:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long p,a;
long long gcd(long long a,long long b)
{
if (!b) return a;
else return gcd(b,a%b);
}
void solve(long long p1,long long a1,long long p2,long long a2,long long &p,long long &a)
{
if (p1<p2) swap(p1,p2),swap(a1,a2);
p = p1 / gcd(p1,p2) * p2;
while (a1 <= p && a1 % p2 != a2)
a1 += p1;
if (a1>p) p=-1,a=-1;
else a=a1;
}
int main()
{
int n;
cin >> n;
cin >> p >> a;
a%=p;
for (int i=2;i<=n;i++)
{
long long pp,aa;
cin >> pp >> aa;
aa%=pp;
solve(p,a,pp,aa,p,a);
}
cout << a << endl;
}
Day 3
BSGS(baby step giant step)
/*
给你三个数a,b,p,其中p为质数
求满足a^x%p=b的x的最小正整数解
*/
#include<bits/stdc++.h>
using namespace std;
/*int solve(int a,int b,int p){//O(p)
int v=1;
for(int i=0;i<p-1;i++){//a^(p-1)=a^0
if(v==b)return i;
v=1ll*v*a%p;
//if(v==1)return -1;//和a的0次方一样,可能会死循环
}
return -1;
}
a^0,a^1,a^2......a^(p-2)
设s为每组的大小
第一组:a^0,a^1,a^2......a^(s-1)
第二组:a^s,a^(s+1),a^(s+2)......a^(2s-1)
第三组:a^2s,a^(2s+1),a^(2s+2)......a^(3s-1)
第一组暴力for循环寻找是否有数%p=b
如果b在第二组出现了,那么他在第一组对应的位置就是b*a^(-s)
查找第二组有没有b,就查找第一组是否有b*a^(-s)
查找其余的组中是否有b出现,只需查找第一组是否有对应的该数
*/
int ksm(long long x,long long y,long long p){//计算x^y%p(快速幂)
if(y==0) return 1;
long long z=ksm(x,y>>1,p);//y>>1等同于y/2
z=1ll*z*z%p;
if(y&1) z=1ll*z*x%p;//y&1等同于y%2==1
return z;
}
int solve(long long a,long long b,long long p){
long long s=sqrt(p);
long long v=1;
set<int> se;
for(long long i=0;i<s;i++){
se.insert(v);
v=1ll*v*a%p;
}
//O(p/s)
for(long long i=0;i*s<=p;i++){//看答案是否在第i行里面
//要看b*a^(-i*s)是否在第零行出现
long long c=1ll*b*ksm(ksm(a,i*s,p),p-2,p)%p;
if(se.count(c)!=0){
long long v=ksm(a,i*s,p);//第i行的第一个数
for(long long j=i*s;;j++){//O(s)
if(v==b)return j;
v=1ll*v*a%p;
}//暴力在第i行寻找答案
}
}
return -1;
}
//O(max(p/s,s)) 所以s=sqrt(p)
long long p,b,a;
int main(){
cin>>a>>p>>b;
while(a||b||p){
a%=p;
b%=p;
long long l=solve(a,b,p);
if(l==-1)cout<<"No solution"<<endl;
else cout<<l<<endl;
cin>>a>>p>>b;
}
/*cin>>p>>b>>n;
long long l=solve(b,n,p);
if(l==-1)cout<<"no solution"<<endl;
else cout<<l<<endl;
return 0;*/
}
排列&&组合
/*
加法:同一阶段
乘法:不同阶段
排列&&组合:
排列:
n个人中选m个人去排队(考虑顺序)
P(n,m)=n*(n-1)*(n-2)*......*(n-m+1)
简化: (n!)/((n-m)!)
组合:
n个人选m个人出去(不考虑顺序)
C(n,m)=(n*(n-1)*(n-2)*......*(n-m+1))/m!
简化:P(n,m)/m!
即:n!/(m!*(n-m)!)
性质:
1.C(n,0)=C(n,n)=1
2.C(n,m)=C(n,n-m)
3.C(n,m)=C(n-1,m-1)+C(n-1,m)
第一个物品选/不选,同一阶段,所以相加
*/
for(in i=0;i<=n;i++){
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
}
//C数组其实就是一个杨辉三角()
//从另一个方面说,杨辉三角中的每一个数都是一个组合数
/*
性质4:C(n,0)+C(n,1)+C(n,2)+......+C(n,n)=2^n
选任意多个东西的方案数,每个都有选或不选两种可能,而两种选不选之间没有联系,所以用乘法
5.C(n,0)-C(n,1)+C(n,2)-C(n,3)+......=0;
所有偶数位的和等于上一行所有位数的和,奇数也同理,所以加上偶数减去奇数,就相当于加上上一行再减去下一行,就是0
6.把C(n,m)展开k次:
C(n,m)=C(k,0)*C(n-k,m-k+0)
+C(k,1)*C(n-k,m-k+1)
+C(k,2)*C(n-k,m-k+2)
...................
+C(k,k)*C(n-k,m-k+k)
即: k
Σ C(k,i)*C(n-k,m-i)
i=0
*/
二项式定理
/*
(x+y)^0=1
(x+y)^1=x+y;
(x+y)^2=x^2+2xy+y^2
(x+y)^3=x^3+3x^2y+3xy^2+y^3
其实他们的系数就是杨辉三角
而第n行x的系数分别为n~0
第n行y的系数分别为0~n
这个公式就叫——二项式定理
(x+y)^n=C(n,0)*x^n*y^0 + C(n,1)*x^(n-1)*y^1 + C(n,2)*x^(n-2)*y^2+...... + C(n,n)*x^0*y^n
||
||
||
n
ΣC(n,i)*x^(n-i)*y^i 计算i从0~n之间的时候,该多项式的和
i=0
卢卡斯定理
/* lucas定理
转进制,不断除进制p,最后倒着连接余数
例:25变为3进制:
3|25............1
---
3|8...........2
---
2
所以25的3进制为221
n=25,m=12,p=3
25的三进制为221,12的三进制为110
C(25,12)%p=C(2,1)*C(2,1)*C(1,0)%3
=2*2*1%3
=4%3
=1
C(n,m)%p=
*/
cin>>n>>m>>p;
//n,m<=10^9 p<=100且为质数
for(int i=0;i<=p;i++){
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%p
}
int lucas(int n,int m){//C(n,m)%p
//O(log p)
while(n!=0){
x[0]++;//p进制下共有多少位
x[x[0]]=n%p;//记录余数
n=n/p;
}
while(m!=0){
y[0]++;
y[y[0]]=m%p;
m=m/p;
}
//x[1] x[2] ... x[x[0]] 就是n的p进制从低到高的表示
//y[1] y[2] ... y[y[0]] 就是m的p进制从低到高的表示
int ans=1;
for(int i=1;i<=max(x[0],y[0]);i++)
ans=1ll*ans*C[x[i]][y[i]]%p;
return ans;
}
组合数取模
//组合数取模
//n,m <=1000
for(int i=0;i<=n;i++){
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
}
//n,m<=10^6 p是质数
fac[0]=1;
for(int i=1;i<=1000000;i++)
fac[i]=1ll*fac[i-1]*i%p;
C(n,m)=1ll
fac[n]*ksm(fac[m],p-2,p)%p*ksm(fac[n-m],p-2,p)%p;
//n<=10^9 m<=10^3
for(int i=1;i<=m;i++){
fenmu[i]=i;
fenzi[i]=n-i+1;
}
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++){
int g=__gcd(fenzi[i],fenmu[j]);
fenzi[i]/=g;
fenmu[j]/=g;
}
int ans=1;
for(int i=1;i<=m;i++)
ans=1ll*ans*fenzi[i]%p;
//n,m<=10^9 p是<=100的质数
//见(lucas)
log
/* log (a*b)=log a+logb
log (a/b)=log a-logb
log C(n,m)=log (n!/(m!(n-m)!))
=log n!-log m!+log (n-m)!
*/
fac[0]=0;
for(int i=1;i<=1000000;i++)
fac[i]=fac[i-1]+log(i);
//log i!=log(i-1)!+log(i)
double logcnm(int n,int m){
//log C(n,m)=log n!-log m!-log (n-m)!
return fac[n]-fac[m]-fac[n-m];
}
C(n1,m1)<C(n2,m2)
logcnm(n1,m2)<logcnm(n2,m2);
抽屉原理
/*
抽屉原理:
基本:把n个东西放到n个抽屉里,一定有一个抽屉至少有2个东西
拓展:把kn+1个东西放到n个抽屉里,一定有一个抽屉至少有k+1个东西
Problem:
给定n个抽屉,要求从中选出任意多个数,使得他们和为c的倍数
c<=n<=10^5
洛谷P2218
*/
容斥原理
/*
容斥原理(n个圈的容斥原理)
两个圈:
A1学语文的人,A2学数学的人
A1∩A2:同时学语文数学的人
A1∪A2:学语文或者数学的人(总人数)
|A1∪A2|=|A1|+|A2|-|A1∩A2|
∩:交集符号 ∪:并集符号
三个圈;
A1学语文的人,A2学数学的人,A3学英语的人
A1∪A2∪A3:学语文或者数学或者英语的人(总人数)
|A1∪A2∪A3|=|A1|+|A2|+|A3| -|A1∩A2|-|A1∩A3|-|A2∩A3| +|A1∩A2∩A3|
四个圈 :
A1学语文的人,A2学数学的人,A3学英语的人,A4学OI的人
A1∪A2∪A3∪A4:学语文或者数学或者英语或者OI的人(总人数)
|A1∪A2∪A3∪A4|=|A1|+|A2|+|A3|+|A4|
-|A1∩A2|-|A1∩A3|-|A1∩A4|-|A2∩A3|-|A2∩A4|-|A3∩A4|
+|A1∩A2∩A3|+|A1∩A2∩A4|+|A1∩A3∩A4|+|A2∩A3∩A4|
-|A1∩A2∩A3∩A4|
例题:有n对夫妻共2n个人,请问有多少种坐法?
约束:1.一对夫妻坐着不能相邻
2.旋转相同算一种方案
*/