1. 程式人生 > >noip級別模板小復習

noip級別模板小復習

不能 目標 eap 要求 記憶化搜索 考點 https st表 bst

不是很noip的知識點就不寫了。

dij什麽的太easy就不寫了。

縮點

  • 註意\(Tarjan\)在縮邊雙和求強聯通分量時候的區別。
  • 一個要判斷是否在棧內一個不要。
  • 最後\(topsort\)\(dp\),或者記憶化搜索,但是一定要記得初值為\(-1\)
  • 考慮圖不聯通。

負環

  • 考慮圖不聯通。
  • 一開始\(dis=0\),判斷最短路長度大於\(n\)會好一些。
  • \(dfs\)\(spfa\)是指數級的。

ST表

  • 註意是\(i\)\(i+2^k-1\)
  • 所以預處理的時候不要減1,因為已經減過了。查詢的時候要加1因為要把減去的1去掉。
Mx[j][i]=max(Mx[j-1][i],Mx[j-1][i+(1<<(j-1))]);
printf("%d\n",max(Mx[k][l],Mx[k][r-(1<<k)+1]));
  • \(O(n)\)預處理\(log\)

線性基

  • 用於查詢多個數異或問題,本質是高斯消元,也可以用來解方程(\(flash\)的考試題)。
  • 記得\(1ll\)線性基的值域與原數組的值域相同,且各個之間線性無關。
  • 如果要查詢某個數,就是查找某個數是否可以由這\(n\)個數中任一個數異或得到。首
  • 從高到低掃這個數的每一位,如果這第\(i\)位為\(1\),就異或上\(P_i\),然後知道處理到最後一位。如果變成 \(0\) 了,那麽就是可以的。
  • 查詢第\(k\)大數。
  • 查詢異或集合中k小值
  • 我們考慮改造一下線性基,使得每一位互相獨立。
  • 如果\(j<i\),且\(p_i\)的第\(j\)
    位是\(1\),就把\(p_i\ xor\ p_j\)
  • 這樣,對於二進制的每一位\(i\)。只有\(p_i\)這一位是\(1\),其他的都是\(0\)
  • 同樣,這個線性基的本質也是沒有改變的。
  • 我們查詢的時候,將\(k\)進行二進制拆分,如果第\(i\)位是\(1\),就異或上線性基中第\(i\)個元素,最終得出的答案就是\(k\)小值。
  • 此外,需要對非滿秩的矩陣進行特判。因為其存在\(0\)的結果,如果要求最小,那麽就是\(0\)
  • 如果不是,那麽就是求當前矩陣下的第\((k-1)\)小。

splay 區間反轉

  • \(lct\)一樣,註意棧序下放標記
S[S[0]=1]=x;
for(R i=x;fa[i];i=fa[i])S[++S[0]]=fa[i];
while(S[0])push(S[S[0]--]);
  • 一定要記得先\(find\)到目標點再轉到根而不是直接做。這裏的\(find\)和整體二分是不一樣的!
push(x);
if(k<=sz[ls])x=ls;
else if(k==sz[ls]+1){spl(x,gl);return x;}
else k-=(sz[ls]+1),x=rs;
  • 提醒幾個常見小錯誤:
void rot(R x){
    R y=fa[x],z=fa[y],k=son(x);
    ch[z][son(y)]=x,fa[x]=z;
    ch[y][k]=ch[x][k^1],fa[ch[x][k^1]]=y;
    ch[x][k^1]=y,fa[y]=x;upd(y);
}
  • \(rot\)\(upd(y)\),而不是\(upd(x)\),如果都\(upd\)要先\(y\)\(x\),不要搞反。
  • 註意一開始要先記下來\(x\)是哪一個兒子,然後先拆開,再接起來。
for(R y=fa[x];y!=gl;rot(x),y=fa[x])
    if(fa[y]!=gl)son(x)^son(y)?rot(x):rot(y);
upd(x);if(!gl)rt=x;
  • 記得判斷\(y\)\(gl\)的關系再決定轉一次還是兩次還是不轉。
  • 一定記得更新\(x\)\(rt\)

splay 普通平衡樹

  • 每次打這個都像在做模擬題……。
  • 兩個log的樹狀數組把一個log的splay掉起來打
  • treap只會過兩個月現在早就忘了
  • 太麻煩了,還不如樹狀數組或者線段樹。
  • 反正你又沒有區間反轉。
  • 太熱了不寫了咕咕

樹鏈剖分

  • 剖分之後一般是搞個線段樹對\(dfn\)序維護。
  • 樹上路徑就暴力跳重鏈條,兩個\(log\)
  • 子樹信息就直接是\(dfn\)\(dfn+sz-1\)的連續區間,一個\(log\)
  • 如果是維護兒子信息就是\(bfs\)序 [SDOI2012]集合
  • 但是我不會啊,咕。

倍增

  • 太普及了。

左偏樹

  • 可並堆,註意不能路徑壓縮。
  • 合並的時候根據堆的屬性來判斷,合並在右子樹。
  • 然後強制向左偏,我的習慣是深度向左偏。
  • 記得更新\(d_i=d_{rs}+1\)
  • 刪除元素就把兩個兒子並起來。

kmp

  • 核心思想是嘗試匹配
  • \(next\)的時候是
j=f[i-1];
while(j>=0&&T[i]!=T[j+1])j=f[j];
if(T[i]==T[j+1])f[i]=j+1;
else f[i]=-1;
  • 也就是不斷嘗試能否接上一個新的後綴,否則就不斷跳\(next\),直到為\(-1\)
  • 查詢的時候是j=f[j-1]+1;,也就是往前走一個,再調\(next\),再往後走一個,也就是\(j\)失配,\(j-1\)配對好了,那麽利用\(j-1\)\(next\),再往後走一個。

AC自動機

  • 主要思想是\(fail\)樹。
  • 先建好\(trie\),然後建\(fail\),然後每次匹配的時候都把\(fail\)的信息都收集一邊。

trie

  • 難道你會了\(ac\)自動機還不會\(trie\)??
  • 可持久化:和主席樹差不多,序列就是相差,樹上就是減去兩倍\(lca\)
  • 啟發式合並:和線段樹啟發合並差不多,也是一個\(merge\)

最小生成樹

  • 本來想補一下\(B\)算法。
  • 但是咕咕了。

dinic

  • 記得當前弧優化,邊從\(2\)開始。
  • 主要技巧在建圖,後面都是板子。

最小費用最大流

  • 同上。

主席樹

  • 動態開點,一般和別的數據結構結合在一起。
  • 序列右邊繼承左邊,樹上兒子繼承父親。

點分治

  • 你家\(noip\)考點分治??咕咕。

manacher

  • 記錄最遠到達的位置和中心。
  • 然後就知道了當前點的半徑下界是對稱過去的半徑。
  • 然後暴力更新當前半徑,更新最遠距離和中心。

模擬退火

  • 系統鐘
db Tim(){return (db)clock()/(db)CLOCKS_PER_SEC;}
  • 生成一個於\(T\)大小相關的隨機,帶正負。
#define RD T*(rand()*2-RAND_MAX)
  • 接受更劣解的概率
exp((ans-now)/T)*RAND_MAX>rand())
  • 註意,\(now\)是當前答案,\(ans\)是當前\(sa\)的最優解,記得保存全局最優解\(bst\)
  • 隨機數組
random_shuffle(x+1,x+n+1);

CDQ

  • 每次強制計算跨過中點的貢獻。

kdtree

  • 註意替罪羊的重構方法。

最小循環表示法

  • 今天才學。
  • 先倍長,初始時,讓\(i=0\)\(j=1\)\(k=0\),其中\(i\)\(j\)\(k\)表示的是以\(i\)開頭和以\(j\)開頭的字符串的前k個字符相同。
  • 分為三種情況
  • 1.如果\(str[i+k]==str[j+k]\) \(k++\)
  • 2.如果\(str[i+k] > str[j+k]\) \(i = i + k + 1\),即最小表示不可能以\(str[i->i+k]\)開頭。
  • 3.如果\(str[i+k] < str[j+k]\) \(j = j + k + 1\),即最小表示不可能以\(str[j->j+k]\)開頭。
  • 那麽只要循環\(n\)次,就能夠判斷出字符串的最小表示是以哪個字符開頭。
  • 為什麽當\(str[i+k] > str[j+k]\),\(i=i+k+1\),最小表示不可能以\(str[i->i+k]\)開頭,讓我們來舉個栗子。
  • 如下圖,當\(i=1\)\(j=5\)\(k=3\)時,\(str[i+k] > str[j+k]\)
  • 首先有\(S1S2S3 == S5S6S7\)\(S4 > S8\)
  • 那麽以字符\(S2\)開頭肯定不如以字符\(S6\)開頭更優,因為\(S4 > S8\)啊。

莫隊

  • 太熱了不寫了。

noip級別模板小復習