luogu4775[NOI2018]情報中心 虛樹 線段樹合併

[NOI2018]情報中心

題目傳送門

分析

即zjoi線圖之後又一道兩百行代碼的題。
其實luogu題解裏面的那份標解已經足夠清楚了,自己寫一遍只是爲了加深理解。

題目大意:給定一棵帶邊權的樹,給定樹上的多條鏈,每條鏈有費用,求有公共邊的兩條鏈並的邊權和-費用的最大值。

這道題的一個核心思路是考慮交邊的狀態,不難發現如下兩種情況:
在這裏插入圖片描述
在這裏插入圖片描述
沒錯我盜的圖,略略略
對於沒有公共Lca的鏈,他們的鏈交是一條直上直下的鏈
對於有公共Lca的鏈,他們的鏈交一條沒有任何特殊性質的鏈
對於前者,其特殊性在於鏈交,對於後者,公共的Lca本身就是很特殊的性質。
分情況討論這兩種情況

Case1

在這裏插入圖片描述
不難發現,我們可以將每條鏈都拆成直上直下的兩條鏈。我們把一條鏈的邊權值加到另一條鏈上,這樣我們可以把問題直接轉化成所有鏈都是直上直下的情況。

一般統計這類問題,都要找基準點。考慮將紅點,也就是交點的下端作爲基準點統計答案。
考慮枚舉所有的紅點。那麼對於每個紅點,答案就是
Len1+Len2C1C2DRed+max(DGreen,DBlue)Len_1+Len_2-C_1-C_2-D_{Red}+max(D_{Green},D_{Blue})
其中u,vu,v是兩條鏈的下端點,C1,C2C_1,C_2是花費,DxD_x表示xx的帶權深度。
對於某個紅點,DRedD_{Red}是常數。
考慮對於紅點的每個子樹開一棵線段樹,每條鏈以上端點深度爲下標,考慮啓發是合併。合併的時候,考慮某個線段樹節點的左右子樹,右子樹的DD肯定大於左子樹的DD,那麼答案就是Max(Len1+C1+D1)+Max(Len2+C2)DRedMax(Len_1+C_1+D_1)+Max(Len_2+C_2)-D_{Red}
線段樹中維護fx,0/1=Lenx+Cx(+Dx)f_{x,0/1}=Len_x+C_x(+D_x)的最大值即可。

Case2

在這裏插入圖片描述
稍微麻煩了一些。我們先考慮所有Lca都在同一個點上的情況。
我們仍然考慮將兩個藍點u,vu,v的答案統計在紅點RedRed上。
2Ans=Len1+Len2+Du+Dv2DRed+Dist(pu,pv)2Ans=Len_1+Len_2+D_u+D_v-2D_{Red}+Dist(p_u,p_v)
其中Dist(pu,pv)Dist(p_u,p_v)表示兩個藍點對應綠點的帶權距離。
一個非常神仙的做法是,新建兩個節點pu,pvp'_u,p'_vpu,pvp_u,p_v連邊,邊權就是Len1+DuLen_1+D_uLen2+DvLen_2+D_v
就是求Dist(pu,pv)2DRedDist(p'_u,p'_v)-2D_{Red}的最大值。
這是一個最遠點對問題。
我們考慮維護紅點的不同子樹內藍點對應的綠點集合的最遠點對。
將所有子樹合併起來即可得到紅點的最遠點對。合併的時候統計答案。
因爲將兩個最遠點對集合合並可以O(6)O(6)兩兩匹配比較做。
LcaLca不在同一個點上的時候,暴力記錄一下藍點所在的位置,對每個點跑個虛樹即可。
總複雜度O(nlogn)O(nlogn)
本題的破題關鍵在於:
1.信息的形式化表達。
採用紅點作爲基準點用盡量少的變量轉化原問題。
2.變量的分離處理和維護。
將不同的變量的信息分離並維護,把複雜度從O(n2)O(n^2)降到O(nlog)O(nlog)

代碼

當然還考察了選手的代碼功底和數據結構功底啦!

#include<bits/stdc++.h>
const int N = 1e5 + 10;
typedef long long LL;
const LL oo = 1e17;
LL ri() {
    char c = getchar(); LL x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
template<typename T>void cmax(T &a, T b) {a = std::max(a, b);}
int d[N], ps[N], Lg[N], de[N], sz[N], ds[N], fa[N], pr[N], nx[N], to[N], bin[20], mn[20][N], tot, tp, n;
LL D[N], ans;
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
int Be(int u, int v) {return de[u] < de[v] ? u : v;}
int Lca(int u, int v) {
    u = ps[u]; v = ps[v];
    if(u > v)
        std::swap(u, v);
    int t = Lg[v - u + 1];
    return Be(mn[t][u], mn[t][v - bin[t] + 1]); 
}
LL Dis(int u, int v) {
    if(!u || !v) return -oo;
    return D[u] + D[v] - (D[Lca(u, v)] << 1);
}
void Dfs(int u) {
    de[u] = de[fa[u]] + 1; D[u] += D[fa[u]];
    mn[0][++tot] = u; ps[u] = tot; sz[u] = 1; ds[u] = 0;
    for(int i = pr[u]; i; i = nx[i]) {
        Dfs(to[i]); mn[0][++tot] = u;
        sz[u] += sz[to[i]];
        if(sz[to[i]] > sz[ds[u]]) 
            ds[u] = to[i];
    }
}
void Pre() {
    Dfs(1); bin[0] = 1;
    for(int i = 1;(bin[i] = bin[i - 1] << 1) <= tot; ++i) 
        for(int j = 1;j + bin[i] - 1 <= tot; ++j)
            mn[i][j] = Be(mn[i - 1][j], mn[i - 1][j + bin[i - 1]]);
    Lg[0] = -1;
    for(int i = 1;i <= tot; ++i)
        Lg[i] = Lg[i >> 1] + 1;
    d[1] = 1;
    for(int i = 2;i <= n; ++i)
        d[i] = ds[fa[i]] == i ? d[fa[i]] : i;
}
int Jump(int u, int c) {
    for(;d[u] != d[c]; u = fa[d[u]]) 
        if(fa[d[u]] == c)
            return d[u];
    return ds[c];
} 
namespace Diff {
    const int T = 2e6 + 10;
    LL mx0[T], mx1[T], w0, w1;
    int rt[N],  ls[T], rs[T], sz, A; 
    int Newnode() {
        ++sz;
        mx0[sz] = mx1[sz] = -oo; 
        ls[sz] = rs[sz] = 0;
        return sz;
    }
    void Ins(int &p, int L, int R, int x) {
        if(!p) p = Newnode();
        cmax(mx0[p], w0); cmax(mx1[p], w1);
        if(L == R) return ; int m = L + R >> 1;
        x <= m ? Ins(ls[p], L, m, x) : Ins(rs[p], m + 1, R, x);
        cmax(ans, mx0[ls[p]] + mx1[rs[p]] - D[A]);
    }
    void Up(int p) {
        mx0[p] = std::max(mx0[ls[p]], mx0[rs[p]]);
        mx1[p] = std::max(mx1[ls[p]], mx1[rs[p]]);
    }
    void Del(int &p, int L, int R, int x) {
        if(L == x) return p = 0, void();
        int m = L + R >> 1;
        x <= m ? rs[p] = 0, Del(ls[p], L, m, x) : Del(rs[p], m + 1, R, x);
        Up(p);
    }
    int Merge(int u, int v, int L, int R) {
        if(!u || !v) return u | v;
        if(L == R) {
            cmax(mx0[u], mx0[v]);
            cmax(mx1[u], mx1[v]);
            return u;
        }
        cmax(ans, mx0[ls[u]] + mx1[rs[v]] - D[A]);
        cmax(ans, mx0[ls[v]] + mx1[rs[u]] - D[A]);
        int m = L + R >> 1;
        ls[u] = Merge(ls[u], ls[v], L, m);
        rs[u] = Merge(rs[u], rs[v], m + 1, R);
        return Up(u), u; 
    }
    void Ins(int u, int c, LL w) {
        w0 = w; w1 = w + D[c]; A = u;
        Ins(rt[u], 1, n, de[c]);
    }
    void Restore() {
        sz = 0;
        for(int i = 1;i <= n; ++i)
            rt[i] = 0;
    }
    void Work() {
        for(int i = n; i > 2; --i) {
            Del(rt[i], 1, n, de[i] - 1); A = fa[i]; 
            rt[fa[i]] = Merge(rt[fa[i]], rt[i], 1, n);
        }
        Restore();
    }
}
namespace Same {
    struct Node {
        int u; LL w;
        Node(int _u = 0, LL _w = 0) : u(_u), w(_w) {}
    };
    LL Dis(Node u, Node v) {return ::Dis(u.u, v.u) + u.w + v.w;}
    struct Data {
        int u; Node v;
        Data(int _u = 0, Node _v = Node(0, 0)) : u(_u), v(_v) {}
    };
    std::vector<Data>vec[N];
    bool cmp(Data a, Data b) {return ps[a.u] < ps[b.u];}
    struct MaxDis {
        Node u, v;
        MaxDis(Node _u = Node(), Node _v = Node()) : u(_u), v(_v) {}
        bool operator < (MaxDis a) const {return Dis(u, v) < Dis(a.u, a.v);}
        void Union(MaxDis &a, int c) {
            if(!u.u)
                return *this = a, a = MaxDis(), void();
            MaxDis x(u, a.u);
            cmax(x, MaxDis(u, a.v));
            cmax(x, MaxDis(v, a.u));
            cmax(x, MaxDis(v, a.v));
            cmax(ans, (Dis(x.u, x.v) >> 1) - D[c]);
            cmax(x, a); cmax(*this, x);
            a = MaxDis();
        }
    }x[N];
    int h[N];
    void Ins(int x, int u, int v, LL w) {vec[x].push_back(Data(u, Node(v, w)));}
    void Up(int f, int u) {x[f].Union(x[u], f);}
    void Ins(int u) {
        if(h[tp] == u)
            return ;
        int c = Lca(h[tp], u);
        for(;tp > 1 && de[h[tp - 1]] >= de[c]; --tp)
            Up(h[tp - 1], h[tp]);
        if(h[tp] != c)
            Up(c, h[tp]), h[tp] = c;
        h[++tp] = u;
    }
    void Work() {
        for(int i = 2;i <= n; ++i) 
        if(vec[i].size()) {
            std::sort(vec[i].begin(), vec[i].end(), cmp);
            h[tp = 1] = i;
            for(Data j : vec[i])  {
                MaxDis Ad(j.v);
                x[j.u].Union(Ad, j.u);
                Ins(j.u);
            }
            for(;tp > 1; --tp)
                Up(h[tp - 1], h[tp]);
            if(tp)
                x[h[1]] = MaxDis();
            vec[i].clear();
        }
    }
}
int main() {
    Diff::mx0[0] = Diff::mx1[0] = -oo;
    for(int T = ri();T--;) {
        n = ri(); ans = -oo;
        for(int i = 1;i < n; ++i) {
            int u = ri(), v = ri(), w = ri();
            add(u, v); D[v] = w; fa[v] = u;
        }
        Pre();
        for(int m = ri(); m--;) {
            int u = ri(), v = ri(); LL w = ri();
            int c = Lca(u, v); LL len = Dis(u, v);
            if(u != c) {
                Diff::Ins(u, c, len - w);
                Same::Ins(Jump(u, c), u, v, len + D[u] - (w << 1));
            }
            if(v != c) {
                Diff::Ins(v, c, len - w);
                Same::Ins(Jump(v, c), v, u, len + D[v] - (w << 1));
            }
        }
        Diff::Work();
        Same::Work();
        if(ans < -1e15)
            puts("F");
        else
            printf("%lld\n", ans);
        tot = tp = 0;
        for(int i = 1;i <= n; ++i)
            pr[i] = 0;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章