「NOIP2018」保衛王國


「NOIP2018保衛王國」

題目描述
有一棵 \(n\) 個點, 點有點權 \(a_i\)\(m\) 組詢問, 每次求欽點兩個節點必須選或者必須不選后的樹上最小點覆蓋。

\(1 \leq n, m \leq 10^5\)

解題思路 :

這個題唯一的意義恐怕是普及了一個還不能算太普及的科技,至少我沒有時間去實現這個東西。當然 \(\text{nqiiii}\) 大爺考場上寫了標算沒寫這個科技就過了是真的強。(不愧是機房里僅次於 \(\text{AK}\)\(\text{zzd}\) 的男人)

我最早接觸這個科技是聯賽前一個星期 \(\text{txc}\) 讓我膜他博客的時候接觸到的,感覺他博客講的非常優秀,現在這里掛一個鏈接傳送一下:撩妹狂魔的博客

說普及科技的原因是因為這個題用動態\(\text{DP}\)來做基本就是一個模板題。首先不考慮詢問,問題是一個簡單的樹形\(\text{DP}\) ,設 \(f[u][0/1]\) 表示以 \(u\) 為根的子樹里面 \(u\) 不選的最小點覆蓋。

\[f[u][0] = \sum_{u\rightarrow v} f[v][1] \\ f[u][1] = a_i +\sum_{u \rightarrow v} \min(f[v][0], f[v][1]) \]

直接處理可能不太方便,不妨先考慮退化成一條 \(i \rightarrow i+1\) 的鏈的情況:

\[f[i][0] = f[i-1][1] \\ f[i][1] = a_i + \min(f[i-1][0], f[i-1][1]) \]

考慮這個 \(\text{DP}\) 只有加法和取 \(\text{min}\) 操作,不妨用重新定義線性變換來描述這個轉移:

\[\begin{bmatrix} 0 & \infty \\ a_i & a_i \end{bmatrix} \begin{bmatrix} f[i-1][0] \\ f[i-1][1] \end{bmatrix} = \begin{bmatrix} f[i][0] \\ f[i][1] \end{bmatrix} \]

這里把乘法重定義為加法,加法重定義為取 \(\min\),這個矩陣的乘法就等價於上式的轉移。然后用線段樹維護一下就可以做原題中鏈的部分分了。

考慮樹的情況,既然會做鏈了,那么只需要樹剖就能搞了。用線段樹維護每一條重鏈上的轉移,轉移的時候需要將這個節點的輕兒子的貢獻寫到轉移矩陣中,那么只需要先預處理出 \(f\) ,設:

\[A_u = \sum_{u\rightarrow v, v \neq mson} f[v][1] \\ B_u = a_u + \sum_{u\rightarrow v, v \neq mson} \min(f[v][1], f[v][1]) \]

原來的線性變換就改為:

\[\begin{bmatrix} A_u & \infty \\ B_u & B_u \end{bmatrix} \begin{bmatrix} f[mson][0] \\ f[mson][1] \end{bmatrix} = \begin{bmatrix} f[u][0] \\ f[u][1] \end{bmatrix} \]

其中 \(mson\) 表示 \(u\) 的重兒子,此時每一個點的 \(f\) 就是其所在重鏈底部到它的所有矩陣乘起來的結果,用線段樹維護即可。

考慮怎么回答這個題的詢問,每次修改可以看做將兩個 \(f[u][s]\) 修改為 \(\infty\) 后求答案,這里等價於修改 \(A_u\)或者\(B_u\) ,考慮一次修改的影響是到當前重鏈的鏈頭的,所以每跳一次輕邊都要重新計算父親的 \(A_u\)\(B_u\) ,這樣相當於是在線段樹上做單點修改,並求出修改完后 \(f[1]\) 的值。考慮輕邊最多跳 \(log\) 次,修改的總復雜度是 \(O(log^2n)\),總復雜度是 \(O(nlog^2n)\)

實際上這個東西還有兩個 \(O(nlogn)\) 的做法,很顯然其中一個是 \(\text{Lct}\),另外一個是叫全局平衡二叉樹的科技,$\text{txc} $的博客里講的非常優秀,大家可以去膜拜一波。


/*program by mangoyang*/
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define inf ((ll)(1e10))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
const int N = 200005;
char str[10];
vector<int> ed[N];
ll f[N][2], g[N][2], ff[N], a[N];
int top[N], sz[N], ls[N], ms[N], dfn[N], n, m, cnt;
struct Matrix{
    ll a[3][3];
    inline Matrix(){ 
        for(int i = 1; i <= 2; i++)
            for(int j = 1; j <= 2; j++) a[i][j] = inf;
    }
    inline Matrix(ll x, ll y){
        a[1][2] = x, a[2][1] = a[2][2] = y, a[1][1] = inf;
    }
    Matrix operator * (const Matrix &A) const{
        Matrix c;
        for(int i = 1; i <= 2; i++)
            for(int j = 1; j <= 2; j++) 
                for(int k = 1; k <= 2; k++)
                    c.a[i][j] = Min(a[i][k] + A.a[k][j], c.a[i][j]);
        return c;
    }
};
struct SegmentTree{
    Matrix s[N<<2];
    #define lson (u << 1)
    #define rson (u << 1 | 1)
    inline void modify(int u, int l, int r, int pos, Matrix x){
        if(l == r) return (void) (s[u] = x);
        int mid = l + r >> 1;
        if(pos <= mid) modify(lson, l, mid, pos, x);
        else modify(rson, mid + 1, r, pos, x);
        s[u] = s[lson] * s[rson];
    }
    inline Matrix query(int u, int l, int r, int L, int R){
        if(l >= L && r <= R) return s[u];
        int mid = l + r >> 1;
        Matrix res; 
        res.a[1][1] = res.a[2][2] = 0, res.a[1][2] = res.a[2][1] = inf;
        if(L <= mid) res = res * query(lson, l, mid, L, R);
        if(mid < R) res = res * query(rson, mid + 1, r, L, R);
        return res;
    }
}Seg;
namespace gao{
    inline void dfs(int u, int fa){
        ff[u] = fa, sz[u] = 1, f[u][1] = a[u];
        for(int i = 0; i < ed[u].size(); i++){
            int v = ed[u][i];
            if(v == fa) continue;
            dfs(v, u), sz[u] += sz[v];
            if(sz[v] > sz[ms[u]]) ms[u] = v;
            f[u][0] += f[v][1];
            f[u][1] += min(f[v][0], f[v][1]);
        }
    }
    inline void dfs2(int u, int fa, int fff){
        top[u] = fff, dfn[u] = ++cnt, g[u][1] = a[u];
        if(!ms[u]){
            Seg.modify(1, 1, n, dfn[u], Matrix(g[u][0], g[u][1]));
            return (void) (ls[fff] = cnt);
        }
        dfs2(ms[u], u, fff);
        for(int i = 0; i < ed[u].size(); i++){
            int v = ed[u][i];
            if(v == ms[u] || v == fa) continue;
            g[u][0] += f[v][1];
            g[u][1] += min(f[v][1], f[v][0]);
            dfs2(v, u, v);
        }	
        Seg.modify(1, 1, n, dfn[u], Matrix(g[u][0], g[u][1]));
    }
    inline ll update(int u){
        Seg.modify(1, 1, n, dfn[u], Matrix(g[u][0], g[u][1]));
        for(u = top[u]; u > 1; u = top[ff[u]]){
            Matrix now = Seg.query(1, 1, n, dfn[u], ls[u]);
            g[ff[u]][0] -= f[u][1];
            g[ff[u]][1] -= Min(f[u][0], f[u][1]);
            f[u][0] = now.a[1][2], f[u][1] = now.a[2][2];
            g[ff[u]][0] += f[u][1];
            g[ff[u]][1] += Min(f[u][0], f[u][1]);
            Seg.modify(1, 1, n, dfn[ff[u]], Matrix(g[ff[u]][0], g[ff[u]][1]));
        }
        Matrix ans = Seg.query(1, 1, n, 1, ls[1]);
        return min(ans.a[1][2], ans.a[2][2]);
    }
}
signed main(){
    read(n), read(m), scanf("%s", str);
    for(int i = 1; i <= n; i++) read(a[i]);
    for(int i = 1, x, y; i < n; i++){
        read(x), read(y);
        ed[x].push_back(y), ed[y].push_back(x);
    }
    gao::dfs(1, 0), gao::dfs2(1, 0, 1);
    for(int i = 1, a, b, x, y; i <= m; i++){
        read(a), read(x), read(b), read(y), x ^= 1, y ^= 1;
        ll tmp1 = g[a][x], tmp2 = g[b][y];
        g[a][x] = inf, gao::update(a);
        g[b][y] = inf; ll ans = gao::update(b);
        if(ans >= inf) puts("-1"); else printf("%lld\n", ans);
        g[a][x] = tmp1, gao::update(a);
        g[b][y] = tmp2, gao::update(b);
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM