【2017.9.16周總結】

Joker_69發表於2017-09-16

Tarjan與縮點

這周做了不少需要先縮點的題。本來10分鐘打出來的Tarjan模板,結果縮點各種錯。以下都是血與淚的教訓

  1. 關於Tarjan中的易錯點:
    • 遇到未訪問的點則用對方的low更新自己的low
    • 遇到訪問過且仍在棧中的點才用對方的dfn更新low
      這兩種情況並不互為補集,不能寫成一個if-else
  2. 重新連邊要考慮是否允許重邊
    若要過濾重邊,最好用set來存邊,用rang-based loop遍歷
    自環一般都要過濾:if (group[u] != group[v]) addedge(u,v);

  3. 做任何拓補序問題之前,先檢視題目是否保證DAG,否則需用Tarjan縮點

點分治

最基礎的靜態點分治框架大概長這個樣子:

bool vis[] = {};

int size[], max[], son[];   //son[x]表示x的重兒子
int dfssize(int x, int fa) {
    //dfs處理以上3個陣列
    //遇到已vis就不再前進了。即 if (!vis[v] && v != fa)
}

dfs(int x, int fa, ...);
cal(int x, ...);

void divide(int x) {
    int Size = dfssize(x,0); //x只是一個入口。這個函式為找root做鋪墊
    int root = x;
    while (max[root]] > Size/2) //找root
        root = son[root];
    dfs(root,...); //解決關於整塊的問題。通常先由dfs把資訊(如dis)記錄到一個陣列或資料結構中,
    cal(root,...); //再由cal()進行一些排序、統計等操作。
    vis[root] = true;
    for_each e(root, v) {
        dfs(v,...); //需要扣除當路徑兩端出自同一子樹的情況
        cal(v,...); //dfs時懾於vis[root]的阻攔,自然不會越出子樹
    }
}