数论这东西,感觉套模板的地方比较多。然后模板又有些类似的地方,想要记忆和区分并使用的话,还是得要对公式的由来进行推导记忆。此处主要对写数论题目时经常会用到的公式的概括。
目录
1.gcd和lcm
列出来是因为太常用了。但因为太常用,就不列代码了。
2.快速幂
快速幂在只需要得到某些特别大的数对某个数的余数时特别有用。有时也变化为求指数的后几位数。(此处还提一下位运算,虽然说运算快一些,但平时也看不出来快在哪里,但有一些时间限制特别严苛的题目,用了位运算和没用就是TLE和AC的区别)
ll quickpow(ll ai,ll bi,ll m)
{
ll all = 1;
while (bi)
{
if (bi & 1)
{
all = all * ai % m;
}
ai = ai * ai % m;
bi >>= 1;
}
return all;
}
例题:POJ—3358
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b)
{
if (b == 0)
{
return a;
}
return gcd(b, a % b);
}
ll quickpow(ll a, ll b, ll m)
{
ll all = 1;
while (b)
{
if (b & 1)
{
all = a * all % m;
}
a = a * a % m;
b >>= 1;
}
return all;
}
ll exgcd(ll a, ll b, ll& x, ll& y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
ll ans;
ans = exgcd(b, a % b, x, y);
ll r;
r = x;
x = y;
y = r - a / b * y;
return ans;
}
ll ola(ll a)
{
ll ans = a;
for (ll i = 2;i * i <= a;i++)
{
if (a % i == 0)
{
ans = ans * (i - 1) / i;
while (a % i == 0)
{
a /= i;
}
}
}
if (a > 1)
{
ans = ans * (a - 1) / a;
}
return ans;
}
int main()
{
ll a, b;
ll s = 1;
while (cin >> a)
{
getchar();
cin >> b;
ll gc;
if (a > b)
gc = gcd(a, b);
else
gc = gcd(b, a);
a = a / gc;
b = b / gc;
ll x=0;
while(b % 2 == 0)
{
b /= 2;
x++;
}
ll num = ola(b);
ll ans;
for (ll i = 1;i * i <= num;i++)
{
if (num % i == 0)
{
if (quickpow(2, i, b) == 1)
{
ans = i;
break;
}
else if (quickpow(2, num / i, b) == 1)
{
ans = num / i;
}
}
}
cout <<"Case #"<<s++<<": "<< x+1 << "," << ans << endl;
}
return 0;
}
3.欧拉函数
1∼N 中与 N互质的数的个数被称为欧拉函数,记为φ ( N )
ll ola(ll a)
{
ll ans = a;
for (ll i = 2;i * i <= a;i++)
{
if (a % i == 0)
{
ans = ans * (i - 1) / i;
while (a % i == 0)
{
a /= i;
}
}
}
if (a > 1)
{
ans = ans * (a - 1) / a;
}
return ans;
}
性质:
1.欧拉函数是积性函数,若m , n m,nm,n互质,则φ ( m n ) = φ ( n ) φ ( m )
2. 2.2.当n nn为质数时,φ ( n ) = n − 1
3. 3.3.当n nn为奇质数时,φ ( 2 n ) = φ ( n )
解决特定的题目,知道公式就能解决,不知道就凉凉。
4.拓展欧几里得
这个就是经常会用在互质的情况下。
ll exgcd(ll a, ll b, ll& x, ll& y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
ll ans;
ans = exgcd(b, a%b, x, y);
ll r;
r = x;
x = y;
y = r - a / b * y;
return ans;
}
5.中国剩余定理
更加迷惑了,虽然知道怎么用,但是看来看去还是没搞懂,大概率是马上又忘了。
ll China(int n,ll *m,ll *a)
{
ll M=1,d,y,x=0;
for(int i=0;i<n;i++) M*=m[i];
for(int i=0;i<n;i++){
ll w=M/m[i];
gcd(m[i],w,d,d,y);
x=(x+y*w*a[i])%M;
}
return (x+M)%M;
}
也是求逆元,逆元就更加迷糊了。
6.总结
菜还是真的菜,感觉写这些题的时候还是认为理解了,但结果碰到换了问法,就又是从头来过,这确实是不得要领。
对于最后两个,拓展欧几里得和中国剩余定理,这个当时就光一题就是一天,结果写完一题,下一题还是不行,套模板虽然说很有用吧,但就是想不出原理是什么了。这两项一直都伴随这求逆元这个东西,这个对于我就个麻烦了,连总结都不知道该怎么总结。
总的来说,不会的最大原因肯定是时间没花上去,但是怎么分配时间就成了大问题,也有可能是没从放松的心态转变过来吧。这是这次的最后一天。希望下次博客就能够得到一个合理的安排,不至于这样。