Day1
列舉與搜尋
列舉
例1:洛谷P1008
- 考慮列舉所有三位數,可過,然而時間複雜度高,容易列舉沒寫不符合題目的答案
- 可找到1:2:3條件來列舉
例2:洛谷P1217
- 列舉a-b中所有迴文質數,TLE
- 列舉a-b中所有質數,TLE*2
- 列舉迴文數(正解)
列舉迴文數:
1. 列舉前一半(總結規律可以發現如果為質數第一位只能為1 3 7 9)
2. 判斷位數奇偶
eg:
列舉了前一半137
奇數:137731
偶數:13731
- 記錄數量,AC
例3:洛谷P1102
雙指標演算法
感覺類似莫隊,就是不斷縮小範圍直到最小區間
#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans;
signed main(){
int a[1000050],n;
a[0]=-10000000;
int vr,vl;
cin>>n>>vl;
vr=vl;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
int j1=0,j0=0;
for(int i=1;i<=n;i++){
while(a[i]-a[j0]>vr) j0++;
while(a[i]-a[j1]>=vl) j1++;
if(j0<j1){
ans+=j1-j0;
//cout<<j1<<' '<<j0<<"\n";
}
}
cout<<ans;
return 0;
}
/*
4 1 1
1 1 2 3
*/
搜尋
搜尋本質也是列舉,差別在於程式碼實現
全排列模型
- DFS列舉全排列
STL大法好algorithm庫的next_permutation(p+1,p+n+1)
函式,且當p為字典序最大的排列時,它會返回0,否則會返回1
兩種方法各有優劣,前者方便減枝,後者實現更為方便。
例1:洛谷P1219
- 列舉全排列,結束
二進位制模型、集合/正整數劃分模型.......
剪枝
- 變換搜尋順序洛谷P1074
- 可行性剪枝
- 物品排序後搜尋(沒有什麼道理,卻能使搜尋變得十分優秀。)
- 最優性剪枝(01揹包問題,但是DFS)
Day2
資料結構
連結串列:
單向連結串列:
模版:洛谷B3631
前項星:
本質:前項星是由多個單項鍊表組成,維護鏈頭陣列,然後可以支援每個點加邊。
struct nod
{
int next,to;
}e[xx*2];
int cnt,h[xx];
void add(int x,int y){
cnt++;
e[cnt]={h[×],y};
h[×]=cnt;
}
void dfs(int x,int y){
for(int i=h[×];i;i=e[i].next)
dfs(e[i].to,x);
}
洛谷P3916
雙向連結串列:
模版:洛谷P1160
就比單向連結串列多了個後繼而已=_=
棧
之所以重要,是因為它其實就是遞迴的邏輯結構,先進後出
STL:
stack<int>stk;
stk.push(1);
stk.pop();
cout<<stk.top()<<"\n";
cout<<stk.size()<<"\n";
洛谷B3614
單調棧
- 始終保持站內元素為單調的
- 比較下標優先
輸入序列:1 4 2 3 5
棧內變化:
5
5 3
5 3 2
5 4
5 4 1
用來求第一個大於某個元素的下標
洛谷P5788
動態陣列vector
顧名思義,它的容量是變化的,是動態的陣列
當內部使用的容量到達了它的上界,他的容量就會翻2倍。
如:
a={} a.push_back(1) 0
a={1} a.push_back(2) 1
a={1,2} a.push_back(3) 2
a={1,2,3} a.push_back(4) 4
a={1,2,3,4} a.push_back(5) 8
a={1,2,3,4,5} 16
... ...
我們也可以分配記憶體a.resize()
也可以釋放記憶體vector<int>().swap(a)
,預設是佔24位元組
佇列
就是模擬現實中的排隊的佇列,先進先出
洛谷B3616
雙端佇列(deque)
就是兩個頭相對的佇列合一塊,STL是支援隨機訪問
然而空間是特別大的,預設分配記憶體有80個位元組,1e6個記憶體總共80多MB
單調佇列
洛谷P1886
原理跟單調棧類似,就是可以取隊首元素可pop隊首了
堆
一種完全二叉樹,可以維護一個最小值或最大值,插入數值時間複雜度為\(O(log_2n\)),刪除同
洛谷P3378
對頂堆
一個序列,我們每次加入一個元素,或者進行詢問。
維護一個初始指標i=0,每次詢問的時候將i=i+1然後詢問第i小的值是多少。
洛谷P1801
哈夫曼樹
洛谷P1090
- 每個數貢獻的次數是他到根的邊數。
- 數大的貢獻較少,即經過的邊數較少。
- 如果把邊順次標號為 0,1,則每個葉子到根的一個字串稱為哈夫曼編
碼。
Day3
動態規劃
簡單DP
洛谷P1216
1.正著遞推
- 可以從頂往下推
f[i][j]=max(f[i-1][j],f[i-1][j-1])+a[i][j];
2.倒著遞推
- 從下往上推
f[i][j]=max(f[i+1][j],f[i+1][j+1])+a[i][j];
揹包DP
給定n個價值為\(V_i\),體積為\(W_i\)的物品,你有一個容積為M的揹包,求怎樣裝物品才能使價值之和最大。
採藥
洛谷P5664
洛谷P5665
區間DP
洛谷P1430
- f[l][r]表示還剩下[l,r]的數,我們先手獲得的最大價值==後手獲得的最大價值
- 可以直接深搜
Day4
圖論
概念
樹:
- 無環
- 連通
- U,V之間唯一一條路
有向無環圖(DAG):
- 可以進行拓撲排序
- 可以進行DP
- 都為二分圖
樹
樹的儲存:
- 記錄每個點的父親
- 每個點存他的兒子
- 當成圖來存
樹的名詞:
- 子樹
- 深度
- 路徑
- 直徑:距離最大的一對點之間的距離為多少
- 重心:以一個點為根,左子樹的點和右子樹的相等則稱該點為一棵樹的重心
二叉樹
- 每個點最多有兩個兒子
- 二叉樹的先序遍歷為它的DFS順序
完美二叉樹:每個點都有兩個兒子
完全二叉樹:點n的父親為n/2,點的兒子為2n、2n+1
DAG最短/長路
直接BFS即可
序
序:
- 傳遞性(a>=b,b>=c,a>=c)
- 自反性(a<=b,b<=a,a=b)
- 反對稱性(a+b=c,c+b≠a)
正序:所有序都可比較
偏序:不是所有序都可比較,但是滿足序的三個性質
DAG所有點預設都是偏序
二分圖
染色(把一個圖染成黑白兩色,讓著圖邊兩端顏色都不同):只需BFS一遍即可
Day5
貪心
就是區域性最優解,看哪個最好選哪個
正確性證明:
- 數學歸納法
例1:
- 定義一個函式f[i]表示取i個線段,最右端最小是多少
- 變換過程中最優解不變
例2:
- f[i]:1~i時間前有多少教師
- g[i]:1~i時間最多的同時講座的數量
h[i]=i
時間同時講座的數量f[0]=0=g[0]
- 往i區間裡放,
f[i-1]=g[i-1]>=h[i-1]
- 能放得進去,
f[i-1]=f[i],g[i-1]=g[i]>h[i]
。- ∴f[n]=g[n]
2.歸納法
3.交換論證
- 任何最優解都可以可以變成貪心的最優解,
- 變換過程中最優解不變
例題:國王遊戲
數學
矩陣
矩陣乘法
省略
矩陣加速遞推
- f1=f2=0
- \(f_n=7f_{n-1}+6f_{n-2}+5n+4×3^n\)
- 求\(f_n\)
高斯消元
特點:
- 兩方程互換,解不變;
- 一方程乘以非零數 k,解不變;
- 一方程乘以數 k 加上另一方程,解不變。
例:
\(\begin{cases} 7x_1+8x_2+9x_3=13 \\4x_1+5x_2+6x_3=12 \\x_1+2x_2+3x_3=11 \end{cases}\)
- 7 8 9==13 → 1 0 04
- 4 5 6==12 → 0 1 05
- 1 2 3==11 → 0 0 16
取模(mod)
\(100 \equiv1\mod {9}\)
\(\equiv\):模運算下的等價
逆元
定義:
- 假設p是一個質數,且\((a, p)=1\),\(a*b=1(mod p)求b\)
- b類似於a的倒數?
- 也就是說,乘上—個b和除以一個a的效果是一樣的b稱作a的逆元,記作\(b=a^{-1}\)
- \(c/a=c*a^{-1}(\mod p)\)
求:
- 費馬小定理
- 如果p為質數,且(a,p)=1;
- 則\(a^{p-1}\equiv(1\mod{9})\)
其他
埃拉託斯特尼篩法
- 求1~n所有素數
O(nlogn)
- 從小到大列舉數把他的倍數篩去
卡特蘭數:
1,1,2,5,14
公式:
\(H_n=\begin{pmatrix}2n\\n\\\end{pmatrix}-\begin{pmatrix}2n\\n-1\\\end{pmatrix}\)
- 只要讓你輸出單個數字的題,暴力程式發現前幾個結果為上面的數列
- 可以試著直接輸出卡特蘭數。
Day6
分治
- 分而治之,就是把一個複雜問題分解為多個簡單的子問題來解決
- 限制為複雜問題要可分
應用:
歸併排序:
基本流程:
sort(l,r);
mid
sort(l,mid);
sort(mid+1,r);
marge(l,mid,r);
逆序對
基本流程:
solve(l,r);
mid
ans+=solve(l,mid);
ans+=solve(mid+1,r);
void solve:
a b
a[i]>b[j]
區間max之和、最大欄位和....
時間複雜度:
最大欄位和:T(n)=2T(n/2)+O(n)=O(nlogn)
區間max之和:T(n)=2T(n/2)+O(1)
例題:
洛谷P1966
二分
二分查詢
簡介:
- 一種查詢陣列元素的方法
- 陣列必須有序
- 時間複雜度O(log(n))
STL
- lower_bound:找到陣列中第一個大於等於x的下標,返回一個地址
- upper_bound:用法同lower_bound
- binary_search:二分查詢一個元素,返回bool值
板子:洛谷P2249
例題:洛谷P1102
-
可以使用第一天的雙指標演算法
-
排序後也可以二分查詢
二分答案
簡介:
-
對答案進行二分查詢
-
要有一個明確的(答案)界限,如
2 1 3 5 6
可以二分(界限為4)
例題:洛谷P1873
洛谷P2440
洛谷P1024
洛谷P2678
while(l<=r){
ull mid=(r+l)/2;//l+(r-l)/2或l+r>>1
if(check(mid)){
l=mid+1;
}else{
r=mid-1;
}
}
cout<<r;
分數規劃
oi-wiki
簡介:
-
每種物品有兩個權值 a 和 b,選出若干個物品使得 \(\displaystyle\frac{\sum a}{\sum b}\)最小/最大。
-
假設我們要求最大值。二分一個答案 mid,然後推式子(為了方便少寫了上下界):
-
\(\displaystyle \begin{aligned} &\frac{\sum a_i\times w_i}{\sum b_i\times w_i}>mid\\ \Longrightarrow&\sum a_i\times w_i-mid\times \sum b_i\cdot w_i>0\\ \Longrightarrow&\sum w_i\times(a_i-mid\times b_i)>0 \end{aligned}\)
-
那麼只要求出不等號左邊的式子的最大值就行了。如果最大值比 0 要大,說明 mid 是可行的,否則不可行。
-
求最小值的方法和求最大值的方法類似.
倍增
樹上祖先:
- 設
f[i][j]
為i往上跳\(2^j\)步 - ∴
f[i][j]=f[f[i][j-1]][j-1]
- (↑的意思:跳\(2^j\)步,需要跳\(2^{j-1}+2^{j-1}\)步)
上面就是倍增的思想
LCA
-
求樹上兩個點最近的公共祖先
-
暴力一步一跳到自己一級祖先容易超時
-
可以用倍增思想來跳
作者:d\(i\)en\(t\)er