ACM基礎數論
數論這塊東西很雜,一個一個定理學,基本就是數學知識。
所以說這種題數學知識比程式碼更重要,主要就是學一些解題必備的數學定理。我用了好久終於把基礎數論裡面的一個一個定理全磨會了,不過不完整,現在光寫一些我學過的比較基礎的數論知識,後面慢慢補充。
拓展歐幾里德演算法
首先是歐幾里得演算法求最大公約數。這個就是輾轉相除法,應該都會。
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
數論的題比較容易涉及大數,所以一般開變數用的都是long long型,程式碼開頭#define ll long long用ll代替一下。再大的數就會用字元陣列進行記錄,文章後面會提到。
這個太簡單了一般不會這麼考,我們要學習的就是拓展歐幾里德演算法。對於ax+by=c的解的問題,當且僅當c%gcd(a,b)!=0時有解。用遞迴的方法求解,證明略。(證明真的很複雜)直接上例題和板子。拓展歐幾里德函式就是板子裡的ggcd函式。
codeforce7C題
A line on the plane is described by an equation Ax + By + C = 0. You are to find any point on this line, whose coordinates are integer numbers from - 5·1018
The first line contains three integers A, B and C ( - 2·109 ≤ A, B, C ≤ 2·109) — corresponding coefficients of the line equation. It is guaranteed that A2 + B2 > 0.
If the required point exists, output its coordinates, otherwise output
題意,求Ax+By+C=0的解,給你ABC,輸出xy,不存在則輸出-1.
#include <iostream>
using namespace std;
#define ll long long
ll x,y;
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
void ggcd(ll a,ll b)
{
ll t;
if (b==0)
{
x=1;
y=0;
return;
}
else
{
ggcd(b,a%b);
t=x;
x=y;
y=t-(a/b)*y;
}
}
int main()
{
int T;
ll n,b,c;
cin>>b>>c>>n;
ll g=gcd(b,c);
if(n%g!=0)
{
cout<<-1<<endl;
return 0;
}
b/=g;c/=g;n/=g;
ggcd(b,c);
x*=-n;y*=-n;//Ax+By=-C
cout<<x<<" "<<y<<endl;
return 0;
}
由題可得公式為Ax+By=-C,套用歐幾里德演算法,注意用之前先/=g(倒數第五行)
哇,瞬間會了一道CF的C題,好開心。
例題2 HDU1576
要求(A/B)%9973,但由於A很大,我們只給出n(n=A%9973)(我們給定的A必能被B整除,且gcd(B,9973) = 1)。
#include <iostream>
using namespace std;
int x,y;
void gcd(int a,int b)
{
int t;
if (b==0)
{
x=1;
y=0;
return;
}
else
{
gcd(b,a%b);
t=x;
x=y;
y=t-(a/b)*y;
}
}
int main()
{
int T,n,b;
cin>>T;
while (T--)
{
cin>>n>>b;
gcd(b,9973);
if (x<0)
x+=9973;
x*=n;
cout<<x%9973<<endl;
}
return 0;
}
矩陣快速冪
先看快速冪。用cmath裡的pow函式計算次方運算太慢,而且不能在計算中取模。於是,快速冪誕生了!
對於a^b,如果b是偶數,ans=a*a,b/2,如果b是奇數,ans=a*a*ans
long long pow_mod(long long a,long long n)
{
long long res=1;
while(n>0)
{
if(n%2==1)//if(n&1)
res=res*a%mod;
a=a*a%mod;
n/=2;//n=n>>1;
}
return res;
}
矩陣快速冪則定義一個矩陣乘法,再進行快速冪運算。
如HDU1005
A number sequence is defined as follows:
f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7.
Given A, B, and n, you are to calculate the value of f(n).
由線性代數知識得
設一個a 2,2的矩陣
a[0][0]=A a[0][1]=B a[1][0]=1 a[1][1]=0
f[n]=a^n-2[0][0]+a^n-2[0][1]
所以先將矩陣乘n次方,再取矩陣前兩個元素相加得f[n]
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD=7;
typedef vector<LL>vec;
typedef vector<vec>mat;
LL n;
mat mul(mat &A,mat &B)
{
mat C(A.size(),vec(B[0].size()));
for( int i=0;i<A.size();i++){
for( int j=0;j<B[0].size();j++ ){
for( int k=0;k<B.size();k++ ){
C[i][j]=(C[i][j]+A[i][k]*B[k][j]);
C[i][j]%=MOD;
}
}
}
return C;
}
mat pow(mat A,LL n)
{
mat B(A.size(),vec(A.size()));
for( int i=0;i<A.size();i++){
B[i][i]=1;
}
while(n>0){
if(n&1)B=mul(B,A);
A=mul(A,A);
n>>=1;
}
return B;
}
void solve(LL a,LL b)
{
mat A(2,vec(2));
A[0][0]=a;
A[0][1]=b;
A[1][0]=1;
A[1][1]=0;
A=pow(A,n-2);
printf("%lld\n",(A[0][0]+A[0][1])%7);
}
int main()
{
LL a,b;
while(~scanf("%lld%lld%lld",&a,&b,&n),a)
solve(a,b);
return 0;
}