1. 程式人生 > >洛谷 P3320: bzoj 3991: LOJ 2182: [SDOI2015]尋寶遊戲

洛谷 P3320: bzoj 3991: LOJ 2182: [SDOI2015]尋寶遊戲

手寫 聯通 題目 倍增 span 很好 數組 type code

題目傳送門:LOJ #2182。

題意簡述:

一棵 \(n\) 個節點的樹,邊有邊權。

每個點可能是關鍵點,每次操作改變一個點是否是關鍵點。

求所有關鍵點形成的極小聯通子樹的邊權和的兩倍。

題解:

有一個結論:DFS 序求出後,假設關鍵點按照 DFS 序排序後是 \(\{a_1,a_2,\ldots ,a_k\}\)

那麽所有關鍵點形成的極小聯通子樹的邊權和的兩倍等於 \(\mathrm{dist}(a_1,a_2)+\mathrm{dist}(a_2,a_3)+\cdots+\mathrm{dist}(a_{k-1},a_k)+\mathrm{dist}(a_k,a_1)\)

畫個圖感性理解一下,應該是很好懂的。

那麽求一下 DFS 序,每次操作相當於往集合裏加入/刪除一個元素。

假設插入 \(x\),它DFS序左右兩邊分別是 \(y\)\(z\)。那麽答案加上 \(\mathrm{dist}(x,y)+\mathrm{dist}(x,z)-\mathrm{dist}(y,z)\) 即可。

刪除同理。還有,求 LCA 就用個倍增或者樹剖吧,Tarjan 離線比較麻煩。

用 STL 自帶的 set 容器維護起來很方便。你也可以手寫樹狀數組/線段樹/平衡樹。

#include <cstdio>
#include <set>

typedef long long LL;
const int MN = 100005;

int N, M;
int h[MN], nxt[MN * 2], to[MN * 2], w[MN * 2], tot;
inline void ins(int x, int y, int z) {
    nxt[++tot] = h[x], to[tot] = y, w[tot] = z, h[x] = tot;
}

int dfn[MN], idf[MN], dfc;
int dep[MN], faz[MN][17];
LL dis[MN];

void DFS(int u, int fz) {
    dfn[u] = ++dfc; idf[dfc] = u; dep[u] = dep[faz[u][0] = fz] + 1;
    for (int j = 1; 1 << j < dep[u]; ++j) faz[u][j] = faz[faz[u][j - 1]][j - 1];
    for (int i = h[u]; i; i = nxt[i]) if (to[i] != fz) dis[to[i]] = dis[u] + w[i], DFS(to[i], u);
}

inline int lca(int x, int y) {
    if (dep[x] < dep[y]) std::swap(x, y);
    for (int d = dep[x] - dep[y], j = 0; d; d >>= 1, ++j)
        if (d & 1) x = faz[x][j];
    if (x == y) return x;
    for (int j = 16; ~j; --j) if (faz[x][j] != faz[y][j])
        x = faz[x][j], y = faz[y][j];
    return faz[x][0];
}

inline LL dist(int x, int y) { return dis[x] + dis[y] - 2 * dis[lca(x, y)]; }

bool vis[MN];
std::set<int> st;
std::set<int>::iterator it;
LL Ans;

int main() {
    scanf("%d%d", &N, &M);
    for (int i = 1, x, y, z; i < N; ++i) {
        scanf("%d%d%d", &x, &y, &z);
        ins(x, y, z), ins(y, x, z);
    }
    DFS(1, 0);
    for (int m = 1, x, y, z; m <= M; ++m) {
        scanf("%d", &x);
        x = dfn[x];
        if (!vis[idf[x]]) st.insert(x);
        y = idf[(it = st.lower_bound(x)) == st.begin() ? *--st.end() : *--it];
        z = idf[(it = st.upper_bound(x)) == st.end() ? *st.begin() : *it];
        if (vis[idf[x]]) st.erase(x);
        x = idf[x];
        LL d = dist(x, y) + dist(x, z) - dist(y, z);
        if (!vis[x]) vis[x] = 1, Ans += d;
        else vis[x] = 0, Ans -= d;
        printf("%lld\n", Ans);
    }
    return 0;
}

洛谷 P3320: bzoj 3991: LOJ 2182: [SDOI2015]尋寶遊戲