暑假清北學堂集訓筆記
day -1
訂票訂得晚只好坐淩晨1點的火車……
day 0
7點鐘到北京了,坐滴滴到酒店,然後去華北電力大學報道,路上看到一輛共享單車,弄了大半天才發現是壞的。。。
報完到就在旁邊的餐廳吃了起來。
day 1
南小鳥(鐘皓曦)講 搜索 分治 倍增 貪心
ST表: f[i][j]表示 從i到i+2^j-1這2^j個位置的元素最大值 初始化: f[i][0]=z[i] 轉移: f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])
LCA(最近公公祖先):f[i][j] 表示從樹上編號為i的點向上走2^j步會走到哪個點 初始化: f[i][0]=father[i] 轉移: f[i][j]=f[f[i][j-1][j-1]
求a,b,LCA時將a置為深度深的那個點,然後一直往上走,知道兩個點深度相同,再將兩個點同時向上走,走到同一個點時,那個點就是他們的LCA
inline void dfs(int now,int f) { for (unsigned i = 0;i < edges[now].size();i++) if (!deep[edges[now][i]] && edges[now][i] != f) { deep[edges[now][i]] = deep[now]+1; father[edges[now][i]][0] = now; dfs(edges[now][i],now); } } void init() { for (int j = 1;(1<<j) <= n;j++) for (int i = 1;i <= n;i++) if (father[i][j-1] != -1) father[i][j] = father[father[i][j-1]][j-1]; } inline int lca(int a,int b) { if (deep[a] < deep[b]) swap(a,b); int i; for (i = 0;(1<<i) <= deep[a];i++); i--; for (int j = i;j >= 0;j--) if (deep[a]-(1<<j) >= deep[b]) a = father[a][j]; if (a == b) return a; for (int j = i;j >= 0;j--) { if (father[a][j] != -1 && father[a][j] != father[b][j]) { a = father[a][j]; b = father[b][j]; } } return father[a][0]; }
快速冪:快速冪是倍增和分治的結合,如:2^50可以分為(2^25)^2 2^25又可以分為(2^12)^2*2一直這樣,我們就可以在O(log n)時間內求出x^n
inline int QuickPow(int x,int y) { if (y == 1) return x; int z = QuickPow(x,y>>1); if (y&1) return z*z*x%mod; return z*z%mod; }
二分查找:二分查找可以在一個單調的序列中用O(log n)的時間找出一個數,它是一種分治
inline int find(int l,int r,int x) { if (r == l) return l; int mid = l+r>>1; if (x < a[mid]) return find(l,mid); return find(mid,r); }
貪心:貪心一般形式是,以某種方式將所有物品排序,排序後按照從小到大進行選擇
搜索:dfs,bfs
搜索優化:
剪枝:把不優於當前最優解的狀態剪掉
卡時:卡時是一種騙分技巧,一般用於最優性問題,當程序準備超時時,直接輸出當前答案,然後結束程序
#include <cstdlib> #include <cstdio> #include <ctime> int t,ans; inline void dfs(...) { if (clock-t >= 990) { printf("%d",ans); exit(0); } ... } int main() { t = clock(); ... dfs(...); printf("%d",ans); return 0; }
晚上測試
第一題是一題不可做的模擬題!!!
我寫裏1.5h+
結果還是沒能調出來
第二題寫了一個四重循環加卡時
第三題沒看
結果
……
0+0+0=0
爆零啦!!!!!!!!!!!!! 有大佬200 Orz %%%
day 2
一上午都在講數論,作為五年級小學生表示聽不懂!!!!!!!!!!!!!!!!!!!
中午南小鳥走了,楊樂來了
楊樂講了一些背包和記憶化搜索
day 3
上午,楊樂又講了一些線性dp
LIS:最長不下降子序列
假設我們需要求以x結尾的最長下降子序列dp[x],由最憂性可得,我們除去最後一個位置(也就是x),還是一段最長下降,
那我們可以枚舉這個子序列的結尾y,最優值就是dp[y]。但需要註意的是,必須保證A[x] < A[Y], x比 Y要低,才滿足下降的要求。
我們從所有枚舉的結果中找到一個最大的即可。
轉移方程:dp[i] = max{dp[j]+1} (j<i a[i]<a[j])
LCS:最長公共子序列
假設我們需要求兩個序列分別以i,j結尾的最長公共子序列
dp[i][j], 接下來我們可以分幾種情況討論:
a[i]不在公共子序列中,那麽長度則等於dp[i-1][j]
b[j]不在公共子序列中,那麽長度則等於dp[i][j-1]
a[i]與b[j]都在子序列中,並且兩者匹配,那麽長度等於dp[i-1][j-1]+1
然後還講了區間dp,二維平面dp,狀態劃分dp
下午楊樂又講了序列劃分dp,樹形dp,狀態壓縮dp……
day 4
楊樂也走了,黃致煥來了
黃致煥講了棧,單調棧,隊列,單調隊列
day4就這樣愉快的結束了
day 5
黃致煥又講了並查集,堆,可並堆,樹狀數組,左偏樹等離奇數據結構。。。
並查集:
並查集是一種樹形數據結構支持合並,查找祖先
查找如下:
inline int find(int x) { //father[x]表示x的祖先,father[x]=x表示x是根節點 if (father[x] == x) return x; //找到根節點 return father[x] = find(father[x]); //祖先的祖先就是我的祖先 }
合並如下:
inline void Union(int x,int y) { father[find(x)] = find(y); } //合並祖先
堆:
定義:一棵滿足以下兩個性質的二叉樹:
1.父節點權值大於等於(大根堆)或小於等於(小根堆)任何一個兒子的權值。
2.每個節點的左子樹和右子樹都是一個堆。
左偏樹:
左偏樹是可並堆的一種
在左偏樹中,每個節點包含兩個屬性:權值和距離(dist)。除了之前二叉堆支持的操作以外,左偏樹還支持合並(merge)操作。
左偏樹的每個節點都滿足做兒子的dist大於右兒子的dist,每個節點的dist的值即為該子樹中與該節點最近的點的距離。
合並如下:
inline int merge(int x,int y) { if (!x || !y) return x|y; //判斷空樹 if (heap[x].key < heap[y].key) swap(x,y); //讓鍵值大的為x heap[x].rson = merge(y,heap[x].rson); //將右兒子與y合並 if (heap[heap[x].lson].dist < heap[heap[x].rson].dist) swap(heap[x].lson,heap[x].rson); //讓dist大的為左兒子 heap[x].dist = heap[heap[x].rson].dist+1; //算dist return x; }
刪除如下:
inline int del(int x) { return merge(heap[x].lson,heap[x].rson); } //將左右兒子合並
然後老師又講了很迷很迷的一些數據結構……
day 6
姜誌豪來了,圖論也來啦
樹的遍歷
BFS:一開始隊列中有一個點,將一個點出隊,將它的子結點全都入隊。
DFS:遞歸到一個點時,依次遞歸它的子結點。
最小生成樹
prim:
先隨機找一個點x作為根,然後維護一個集合S(存已經連通的點)和一個集合D(存當前連接S中所有點的線段,兩端點都在S中的除外)
初始化S={x},D={x所連接的所有邊};
每次在D中選一個權值最小的邊,然後將該邊刪去。該邊所連接的另一個點放入S中,直到S中點數=全部點數。
這裏記頂點數n,邊數m,
時間復雜度:O(m log n)
kruskal:
將邊權從小到大排序,每次選擇邊權最小的,如果當前邊連接的點已經連通了,就不選這條邊。
利用並查集維護是否連通。
m為圖中的邊數
時間復雜度:O(m log m)
最短路算法
floyd:
設dist[i][j]為i~j的距離,則有動態轉移方程:dist[i][j] = max{dist[i][k]+dist[k][j]}
dijkstra:
a.初始時,S只包含源點,即S={v},v的距離為0。U包含除v外的其他頂點,即:U={其余頂點},若v與U中頂點u有邊,則正常有權值,若u不是v的出邊鄰接點,則權值為∞。
b.從U中選取一個距離v最小的頂點k,把k,加入S中(該選定的距離就是v到k的最短路徑長度)。
c.以k為新考慮的中間點,修改U中各頂點的距離;若從源點v到頂點u的距離(經過頂點k)比原來距離(不經過頂點k)短,則修改頂點u的距離值,修改後的距離值的頂點k的距離加上邊上的權。
d.重復步驟b和c直到所有頂點都包含在S中。
spfa:
首先建立一個數組s,s[i]代表從始點到i點所需要的最短距離。
利用一個隊列進行松弛操作:
初始化s[i]=∞;
首先將始點入隊,然後將始點所連接的點x入隊。入隊的時候,將始點到x的距離d賦值給s[i]。對於始點所連接的所有點都進行如下操作,彈出隊首元素。
訪問隊首元素y,將隊首元素y所連接的所有的點都入隊。假如其中一個點為z,則判定一下s[z]是否大於s[y]+< y,z >邊權。如果大於,則松弛一下,把s[y]賦值為較小的那個。
toposort:
入度:有多少條邊指向他
出度:有多少條邊從他發出
先統計每個點的入度,假如a->b,則in[b]++;
用棧來維護入度=0的點。
若棧非空,則在棧中彈出一個元素(並輸出),然後枚舉這個點能到的每一個點將它的入度-1.如果入度=0,則壓入棧中。
如果沒有輸出所有的頂點,則有向圖中一定存在環
強連通分量:
……
晚上,第二波考試
第一題:水題,n^2大暴力
第二題:不會做,仍然n^2大暴力
第三題:想到正解,寫炸了!!!
100+20+30=150 有大佬270 Orz %%%
day 7
Trie樹,KMP,AC自動機……
day 111
離noip還有1周……
暑假清北學堂集訓筆記