数学知识
** 数论里面好多的题多需要用long long --
----------------------------------------------------------------------------------------------------------------
快速幂--快速求出a的k次方模p
typedef long long ll;
ll qmi()
{
ll res = 1;//注意这里必须是res = 1;
while(k)
{
if(k&1) res = res*a%p;
k>>=1;
a = a*a%p;
}
return res;
}
快速幂 -- 求逆元
费马小定理:
----------------------------------------------------------------------------------------------------------------
gcd
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
----------------------------------------------------------------------------------------------------------------
前置知识:
裴蜀定理:对于任意正整数a,b,那么一定存在非零整数x,y,使得ax+by = (a,b)/a和b的最大公约数;
扩展gcd
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x = 1,y = 0;
return a;
}
int d = exgcd(b,a%b,y,x);
y -= a/b*x;
return d;//返回的是a,b的最大公约数,同时也是一组x,y的解;
}
典型应用 : 线性同余方程 -- a*x == b(modm) ---> a*x-m*y == (a,-m) 判断b能够整除(a,-m);
----------------------------------------------------------------------------------------------------------------
质因数
质数--在大于1的整数中,如果只包含1和本身这两个约数,就被称为质数,或者叫素数;
试除法判定一个质数
bool is_prime(int n)
{
if(n<2) return false;
for(int i = 2;i<=n/i;++i)//该处的n/i为最优写法;
{
if(n%i == 0) return false;
}
return true;
}
分解质因数--n中最多只包含一个大于根号n的质数,如果N中出现两个大于根号n的质因子,相乘必定大于n
--见acwing867.
void divided(int n)
{
for(int i = 2;i<=n/i;++i)
{
if(n%i == 0)//i一定是质数--n一定是i的倍数,并且n当中不包含任何2~i-1之间的倍数,所以i当中也不包含任何2~i-1之间
//的质因子;
{
int res = 0;
while(n%i == 0)
{
res++;
n/=i;
}
cout<<i<<" "<<res<<endl;
}
}
if(n>1) cout<<n<<" "<<1<<endl;
}
筛选质因数 -- 对于一个合数x,假设p[j]是x的最小质因子,当i枚举到x/p[j]的时候,这个合数被筛
**用最小质因子筛素数 --所以i%primes[j] == 0 的目的是为了防止重复筛选合数;
int primes[N],cnt;
bool st[N];
void get_primes(int n)
{
for(int i = 2;i<=n;++i)
{
if(!st[i]) primes[cnt++] = i;
for(int j = 0;primes[j]<=n/i;++j)
{
st[primes[j]*i] = true;
if(i%primes[j] == 0) break;
}
}
1.i%p[j] == 0--primes[j]一定是i的最小质因子;
2.i%p[j]!= 0 --p[j]也一定是p[j]*i的最小质因子;
----------------------------------------------------------------------------------------------------------------
求组合数
1--
1<=b<=a<2000 递推;
const int N = 2010,mod = 1e9+7;
int c[N][N];
void init()
{
for(int i = 0;i<N;++i)
{
for(int j = 0;j<=i;++j)
{
if(!j) c[i][j] = 1;
else c[i][j] = (c[i-1][j-1]+c[i-1][j])%mod;
}
}
}
2--
1<=b<=a<=100000 预处理--阶乘思想;
const int N = 100010,mod = 1e9+7;
int fact[N],infact[N];//分别存储分子阶乘和逆元阶乘;
typedef long long ll;
ll qmi(ll a,ll b,ll k)//用快速幂计算逆元--费马小定理--k一定是质数;
{
ll res = 1;
while(b)
{
if(b&1) res = (ll)res*a%k;
b>>=1;
a = (ll)a*a%k;
}
return res;
}
void init()//预处理;
{
fact[0] = infact[0] = 1;
for(int i = 1;i<N;++i)
{
fact[i] = (ll)fact[i-1]*i%mod;
infact[i] = (ll)infact[i-1]*qmi(i,mod-2,mod)%mod;
}
}
3--
1<=b<=a<=10(18次) -- 卢卡斯定理--lucas
typedef long long ll;
int n,p;//p为模的值;
ll qmi(ll a,ll b)//快速幂求逆元;
{
ll res = 1;
while(b)
{
if(b&1) res = res*a%p;
b>>=1;
a = a*a%p;
}
return res;
}
ll C(ll a,ll b)//阶乘思想求组合数
{
ll res = 1;
for(int i = 1,j = a;i<=b;++i,--j)//当i枚举到b的时候,j也从a枚举到a-b+1了;
{
res = res*j%p;
res = res*qmi(i,p-2)%p;
}
return res;
}
ll lucas(ll a,ll b)//卢卡斯算法求最终的结果;
{
if(a<p && b<p) return C(a,b);
else return (ll)C(a%p,b%p)*lucas(a/p,b/p)%p;
}
----------------------------------------------------------------------------------------------------------------
离散化-- 范围广(10^9)--值稀疏
查找--
int find(int x)
{
int l = 0,r = alls.size()-1;
while(l<r)
{
int mid = l+r>>1;
if(alls[mid]>=x) r = mid;
else l = mid+1;
}
return r+1;
}
存储--
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
----------------------------------------------------------------------------------------------------------------
约数--
试除法求约数
vector<int> get_privisors(int x)
{
vector<int> res;
for(int i = 1;i<=x/i;++i)//这里是i== 1,约数包括1和它本身;
{
if(x%i == 0)
{
res.push_back(i);
if( i != x/i) res.push_back(x/i);
}
}
sort(res.begin(),res.end());
return res;
}
约数个数--来源是算术分解定理(k0+1)+(k1+1)+...+(k&+1)因此i从2开始遍历;
unordered_map<int,int> primes;//用哈希记录;
for(int i = 2;i<=x/i;++i)
{
while(x%i == 0)
{
x /= i;
primes[i]++;
}
}
if(x>1) primes[x]++;
ll res = 1;
for(auto p : primes) res = res*(p.second+1)%mod;
约数之和--
unordered_map<int,int> hash;
while(n--)
{
int x;
cin>>x;
for(int i = 2;i<=x/i;++i)
{
while(x%i == 0)
{
x = x/i;
hash[i]++;
}
}
if(x>1) hash[x]++;
}
ll res = 1;
for(auto p:hash)
{
ll a = p.first,b = p.second;
ll t = 1;
while(b--) t = (t*a+1)%mod;
res = t%mod*res%mod;
}
----------------------------------------------------------------------------------------------------------------
欧拉函数--
求1~n中与n互质的数的个数;
while(n--)
{
int a;
cin>>a;
int res = a;
for(int i = 2;i<=a/i;++i)
{
if(a%i == 0)
{
res = res/i*(i-1);
while (a%i == 0) a = a/i;
}
}
if(a>1) res = res/a*(a-1);
cout<<res<<endl;
}
线性筛法求欧拉函数 -- 求1~n中所有数的欧拉函数的和;
void enlers(int n)
{
enler[1] = 1;
for(int i = 2;i<=n;++i)
{
if(!st[i])
{
primes[cnt++] = i;
enler[i] = i-1;
}
for(int j = 0;primes[j]<=n/i;++j)
{
int t = primes[j]*i;
st[t] = true;
if(i%primes[j] == 0)
{
enler[t] = enler[i]*primes[j];
break;
}
enler[t] = enler[i]*(primes[j]-1);
}
}
}
----------------------------------------------------------------------------------------------------------------