鸽笼原理
定理:将n+1个物品放进n个盒子至少一个盒子含有2个及以上的物品。
抽象:将n个物品进行k次划分产生k+1个区间至少有n-k个区间含有n/k个物品
(高中排列组合里的隔板法)
比如:有1500人过生日,至少5人同生日;n个人握手,一定有两人握手次数相同
HDU1205“分糖”
思路:设总糖数位sum,找出n种中数目最大的哪一种,数目设为max。根据鸽笼原理,把max视为挡板的话。
如果满足max数小于sum-max<max-1那么说明必定有两个数目最大种类的糖放在一起了。
否则,必定可以。
拓展:鸽笼原理实际是ramsey原理的特判
ramsey:6个人中至少三个人认识或者至少三个人不认识具体题目:hdu5917
思路:
当节点小于6暴力枚举n的5次幂
大于等于6直接C(m ,n)m是选定子集的节点数,n是总数
杨辉三角和二项式系数
知识点:排列组合公式,二项式系数公式,二项式系数关系
排列组合公式:
二项式系数公式:
对于(ax+b)^n 其中第k项的系数为:C(k-1,n)*a(n-k-1) *bk-1
二项式系数和杨辉三角的关系:
杨辉三角第n行第k项对应C(k,n)
容斥原理
简单来讲就是去重
比如:在埃氏筛法中我们删去非素数时是按倍数删,6是2的倍数也是3的倍数被重复删了两次自然要加回来一次。
重点在推导公式
超大菲波那切数列项
主要运用了数论的知识,包括对数原理和快速幂原理。
要求:输出1e9内的斐波那契数,由于斐波那契数增长十分迅速所以当数大于8位时我们只要输出这个数的前四位和后四位就好。
思路:前四位和后四位时两种做法。前四位:数论利用对数和菲波那切数列通项公式实现。后四位:利用矩阵快速幂和公式实现,由于快速幂过程中得到的数还是会非常大,所以还要进行取模操作。
通项公式:
快速幂公式:
代码:
前四位
#include <bits/stdc++.h>
using namespace std;
int main()
{
double s=(sqrt(5.0)+1.0)/2.0
double ans=-0.5*log(5.0)/log(10.0)+((double)n)*log(s)/log(10.0);
ans-=floor(ans);
ans=pow(10.0,ans);
while(ans<1000)
ans*=10;
cout<<ans<<endl;
return 0;
}
原理:我们利用举个例子
假设给出一个数10234432,那么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
于是就可以只取前面的1023了
后四位:
#include <bits/stdc++.h>
#define mod 100000000+7;
using namespace std;
struct Matrix
{
long long data[2][2];
};
Matrix f={1,0,0,0};
Matrix ini={1,1,1,0};
Matrix Matrix_Multi(Matrix a,Matrix b)
{
Matrix res;
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
res.data[i][j]+=a.data[i][k]*b.data[k][j]%mod;
res.data[i][j]%=mod;
}
}
}
return res;
}
long long fast_pow(int n)
{
Matrix a=f,b=ini;
while(n)
{
if(n&1)
a=Matrix_Multi(a,b);
b=Matrix_Multi(b,b);
n>>=1;
}
return ((a.data[0][0])%1000);
}
int main()
{
int n;
cin>>n;
long long ans=fast_pow(n);
cout<<ans<<endl;
return 0;
}
原理:矩阵相乘
这里给出了很多数学方法,理解起来其实并不困难,难的是将这些方法用于具体题目中