數奧四大項:
非競賽 e.g. 微積分
油宅四大項:
其他 e.g. ???
結論: 數奧國手和油宅有很大的相似之處
VS.
計算 \(a^b\)
複雜度: \(O(b)\)
好像有點慢...
想計算\(a^b\),若已知 \(c=a^{\lfloor \frac{b}{2}\rfloor}\)
若 \(b\) 是偶數,則 \(a^b=c^2\)
若 \(b\) 是奇數,則 \(a^b=a \times c^2\)
複雜度: \(O(logN)\)
int exp(int a, int b, int m){
if(b==0) return 1;
if(b%2){
return a*exp(a,b-1,m)%m;
} else{
int tmp=exp(a,b/2,m);
return tmp*tmp%m;
}
}
int exp(int a, int b, int m){
int c=1, e=a;
while(b>0){
if(b%2){
c=(c*e)%m;
}
e=(e*e)%m;
b/=2;
}
return c;
}
某個看起來很像二維陣列的東西
橫的稱為列
直的稱為行
對應的位置相加
將一 \(m\times n\)的矩陣 \(A\) 和 \(p\times q\)的矩陣 \(B\) 相乘
條件: \(n\) 必須等於 \(p\),相乘後的矩陣 \(C\) 為 \(m\times q\)
且\(C_{ij}=\sum_{k=1}^{n}A_{ik}B_{kj}\)
typedef vector<vector<int> > matrix;
matrix mul(matrix a, matrix b){
matrix c=matrix(a.size(),vector<int>(b[0].size(),0));
for(int i=0;i<a.size();i++){
for(int j=0;j<b[0].size();j++){
for(int k=0;k<a[0].size();k++){
c[i][j]+=a[i][k]*b[k][j];
}
}
}
return c;
}
複雜度: \(O(n^3)\)
還能更快?
矩陣乘法 -> 矩陣冪 -> 矩陣快速冪
作法就把前面實數乘法改成矩陣乘法
複雜度: \(O(n^3logk)\)
https://tioj.ck.tp.edu.tw/problems/2151
去年有趣的校內賽題
\(C_m^n\) : \(n\) 個相異物品取 \(m\) 個的方法數
\(C_m^n=\frac{n!}{m!(n-m)!}\)
\(P_m^n\) : \(n\) 個相異物品取 \(m\) 個排成一排的方法數
\(P_m^n=\frac{n!}{(n-m)!}\)
\(O(N)\)預處理階乘
或是利用性質
\(C_m^n=C_{m-1}^{n-1}+C_m^{n-1}\)
https://tioj.ck.tp.edu.tw/problems/2163
題敘有點長 自己看
詳情見8e7orz的講義
關於"整數"的學問
(有時也會討論有理數)
\(a\equiv b (mod m)\)
表示a除以m的餘數和b除以m的餘數相同
如: \(9\equiv 2 (mod\ 7)\)
加法: \(a\equiv b (mod m) \Leftrightarrow a+c\equiv b+c (mod m) \)
乘法: \(a\equiv b (mod m) \Rightarrow ac\equiv bc (mod m) \)
注意: 不一定能除
整除符號 \(a\mid b\)
表示存在一整數 \(k\) 使得 \(b=ak\)
ex. \(3\mid 12、87\mid 0、-2\mid 22\)
gcd: 最大公因數
lcm: 最小公倍數
\(gcd(a,b)\times lcm(a,b) = a\times b\)
why?
輾轉相除
性質:\(gcd(a,b)=gcd(b,a\% b)\)
當其中一個為0時,gcd就是另一個
int gcd(int a, int b){
if(b==0) return a;
return gcd(b,a%b);
}
複雜度: \(O(logN)\)
Why?
\(ax\equiv b (mod m)\)
不能除怎麼辦?
一數字 \(a\) 在模 \(m\) 下的模逆元 \(a^{-1}\) 滿足
不一定存在! 條件: \(gcd(a,m)=1\)
費馬小定理
給定一質數 \(p\) 與一比 \(p\) 小的數 \(a\),恆有
證明?
\(a^{p-1}\equiv 1\ (mod\ p)\)
\(\Rightarrow a\cdot a^{p-2}\equiv 1 (mod p)\)
所以 \(a^{p-2}\) 即為 \(a\) 的模逆元
擴展歐幾里德(增強輾轉相除)
貝祖定理: 對於兩數 \(a,b\),存在無限多組整數 \(x,y\)
使得 \(ax+by=gcd(a,b)\)
已知 \(gcd(a,m)=1\)
我們想找到一個 \(b\),使得 \(ab\equiv 1 (mod m)\)
存在 \(k\) 使得 \(ab-mk=1\)
給定 \(a,m\),求出 \(b,k\) 即可!
想要求\(ax+by=1\)的解
若已知 \(x',y'\) 滿足 \(bx'+(a\%b)y'=1\)
要怎麼得到解?
void extgcd(int a, int b, int &x, int &y, int &d){
if(b==0){
d = a;
x = 1;
y = 0;
return;
} else {
extgcd(b, a % b, x, y, d);
int nx, ny;
nx = y;
ny = x - a/b * y;
x = nx;
y = ny;
return;
}
}
bool isPrime(int n){
for(int i=2;i*i<=n;i++){
if(n%i==0){
return false;
}
}
return true;
}
複雜度: \(O(\sqrt N)\)
用上一頁的方法一個一個硬做
複雜度: \(O(N\sqrt N)\)
好像有點慢...
做法: 一開始所有數都是質數,
從 \(2\) 開始,將自己所有的倍數都標記為"不是質數"
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|
複雜度: \(N\cdot \sum_{k=1}^{N} \frac{1}{k}=O(NlogN)\)
優化:
最終複雜度: \(N\cdot \sum_{p\leq N} \frac{1}{p}=O(N log log N)\)
跟線性差不了多少(?
const int MAXN=1E5;
bool isPrime[MAXN+1];
void getPrimes(){
for(int i=0;i<=MAXN;i++) isPrime[i]=1;
isPrime[0]=isPrime[1]=0;
for(ll i=2;i<=MAXN;i++){
if(isPrime[i]){
for(ll j=i;i*j<=MAXN;j++){
isPrime[i*j]=0;
}
}
}
}
我們想要每個合數都被篩掉一次!
每個合數\(N\)都有最小質因數 \(p_{min}\)
我們想在 \(i=\frac{N}{p_{min}}\)時被篩掉
若目前在 \(i\) 且沒被篩掉,則 \(i\) 為質數。
維護一個目前的所有質數的陣列,對於每個質數 \(p\) :
\(p\) 會是 \(i\) 的最小質因數,所以後面的數會被 \(p\) 篩掉,不用急!
複雜度: \(O(N)\)
const int MAXN=1E5;
bool isPrime[MAXN+1];
vector<int> primes;
void getPrimes(){
for(int i=0;i<=MAXN;i++) isPrime[i]=1;
for(int i=2;i<=MAXN;i++){
if(isPrime[i]) primes.push_back(i);
for(auto p:primes){
if(i*p>MAXN) break;
isPrime[i*p]=0;
if(i%p==0) break;
}
}
}
維護\(1\)~\(\sqrt N\)的質數,再一個個驗證
原因: 對於一個數字\(N\),至多一個質因數大於\(\sqrt N\)
vector<int> primes; //用前面質數篩法預處理好
vector<pii> factorize(int n){
vector<pii> vec;
pii tmp;
for(auto p:primes){
if(p*p>n) break;
if(n%p==0){
tmp={p,0};
while(n%p==0){
tmp.S++;
n/=p;
}
vec.push_back(tmp);
}
}
if(n>1) vec.push_back({n,1});
return vec;
}
有物不知其數,三三數之剩二,五五數之剩三,七七數之剩二。問物幾何?
下卷第二十六題 物不知數
模 \(m_1\) 為 \(1\)
模 \(m_2\) 為 \(0\)
模 \(m_1\) 為 \(0\)
模 \(m_2\) 為 \(1\)
如果只有兩條式子
推廣到 \(n\) 條式子的情形
定義 \(M_i=\prod_{k=1,k\neq i}^{n} m_k\)
\(\mu_i=M_i^{-1}(mod m_i)\)
則 \(M_i\mu_i\) 在模 \(m_i\) 時為\( 1\),
在模其他的 \(m_j\) 為 \(0\),故構造
一樣先考慮兩條式子
\(m_1k_1-m_2k_2=a_2-a_1\)
\(ax+by=c\) ?
若 \(a_2-a_1\nmid gcd(m_1,m_2)\) 則無解
否則可以求出 \(k_1\) 的值,然後代回去
得到某種 \(x\equiv m_1k_1+a_1 (mod lcm(m_1,m_2))\)
剩下\(n-1\)條式子,可以繼續做下去
大部分是資芽講義抄來的
typedef pair<int,int> pii;
#define F first
#define S second
//內積
int operator*(const pii &a, const pii &b){
return a.F*b.F+a.S*b.S;
}
//外積
int operator^(const pii &a, const pii &b){
return a.F*b.S-a.S*b.F;
}
給定兩線段,求是否相交
可以發現若兩線段相交,只有兩種case:
(1) 一線段的一個端點在另一線段上
(2) 兩線段的兩端點都分別在另一線段兩側
Case 1
Case 2
不相交
四個點只要有一個就可
兩邊都要判
給定一堆點,將他們依照對原點的角度排序
方法一: sort by angle
bool cmp1(pii a, pii b){
return atan2(a.S,a.F)<atan2(b.S,b.F);
}
方法二: sort by cross
bool cmp2(pii a, pii b){
bool f1 = a<pii(0,0);
bool f2 = b<pii(0,0);
if(f1!=f2) return f1<f2;
return a^b>0;
}
基本上建議用方法二,因為不會有浮點數誤差的問題
給定很多點,將所有點用最小的凸多邊形包起來,
稱為凸包。
分別考慮上半部和下半部
將所有點先照座標排序,依序考慮每個點
開一個stack(或是vector)紀錄目前的凸包
類似單調對列的概念,每加進一個點,就看哪些點必須被移除
紅點加入後
移除藍點
紅點加入後
保留藍點