「校内训练 2019-04-23」越野赛车问题 动态dp+树的直径

Posted hankeke

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「校内训练 2019-04-23」越野赛车问题 动态dp+树的直径相关的知识,希望对你有一定的参考价值。

题目传送门

http://192.168.21.187/problem/1236

http://47.100.137.146/problem/1236

题解

题目中要求的显然是那个状态下的直径嘛。

所以这道题有一个非常简单的做法——线段树分治。

直接把每一条边按照 \(l, r\) 的区间放到线段树上进行分治,遍历的时候用并查集维护直径就可以了。

时间复杂度为 \(O(n\log^2n)\)

很早以前就写了这个算法,代码附在了最后,不多讲了。

但是这道题还有一个方法——动态 DP。


线段树分治是给哪些不方便撤销不是按照栈的顺序撤销的操作的问题使用的,如果这个问题支持按照栈的顺序撤销修改,那么可以很好的使用线段树分治。

但是我们发现这个问题实际上不是栈的顺序的修改也可以做。也就是用动态 DP 维护树直径。

\(dp[x][0]\) 表示在 \(x\) 的子树子树中,从 \(x\) 上面那个边出发的最长链。(为什么是上面那个边:这样是否转移可以从 \(x\) 这里看出来,而不是需要在 \(x\) 的父亲那里转移的时候特判,方便 ddp)。

\(dp[x][1]\) 表示 \(x\) 的子树中的直径。

那么转移就是这样的(经典转移):
\[ dp[x][0] = \max_y\in child_x dp[y][0] + 1\\dp[x][1] = \max\\max_y\in child_x dp[y][1], \max_y1, y2 \in child_x dp[y_1][0] + dp[y_2][0]\ \]


设树剖完以后,\(x\) 的重儿子为 \(son_x\),轻儿子集合为 \(light_x\)。令 \(h[x][0] = \max\limits_y\in light_x dp[y][0]\)\(h[x][1] = \max\\max\limits_y\in light_x dp[y][1], \max\limits_y_1,y_2\in light_x dp[y_1][0] + dp[y_2][0]\\)

这样上面的 dp 式子可以改写成:
\[ dp[x][0] = \max\dp[son_x][0], h[x][0]\ + 1\\dp[y][0] = max\ dp[son_x][1], h[x][1], dp[son_x][0] + h[x][0] \ \]
于是转移矩阵就是(定义矩乘为求和后取 \(\max\)):
\[ \beginbmatrix 1 & -\infty & h[x][0] + 1 \\ h[x][0] & 0 & h[x][1] \\ -\infty & -\infty & 0 \endbmatrix \times \beginbmatrix dp[son_x][0] \\ dp[son_x][1] \\ 0 \endbmatrix = \beginbmatrix dp[x][0] \\ dp[x][1] \\ 0 \endbmatrix \]
然后 ddp 就可以了。


关于 \(h\) 的维护:

我们维护两个可删除堆,\(q1[x]\) 记录所有的轻儿子 \(y\)\(dp[y][0]\)\(q2[x]\) 记录所有的轻儿子 \(y\)\(dp[y][1]\)

\(h[x][0]\):显然是取 \(q1[x]\) 里面最大的;

\(h[x][1]\):首先是 \(q2[x]\) 里面最大的,然后再看看 \(q1[x]\) 里面最大的两个数的和是不是更大。


时间复杂度还是 \(O(n\log^2 n)\)。貌似我写的这个 ddp 被线段树分治碾压,但是 thx 写的就碾压了线段树分治。(雾

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) return a < b ? a = b, 1 : 0;
template<typename A, typename B> inline char smin(A &a, const B &b) return b < a ? a = b, 1 : 0;

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) 
    int f = 0, c;
    while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    x = c & 15;
    while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    f ? x = -x : 0;


#define lc o << 1
#define rc o << 1 | 1

const int N = 70000 + 7;
const int INF = 0x3f3f3f3f;

int n, m, dfc;
int dep[N], f[N], siz[N], son[N], dfn[N], pre[N], bot[N], top[N];
int w[N], ans[N];
std::vector<int> v[N];

struct Edge  int to, ne, l, r;  g[N << 1]; int head[N], tot;
inline void addedge(int x, int y, int l, int r)  g[++tot].to = y, g[tot].l = l, g[tot].r = r, g[tot].ne = head[x], head[x] = tot; 
inline void adde(int x, int y, int l, int r)  addedge(x, y, l, r), addedge(y, x, l, r); 

struct Heap 
    std::priority_queue<int> q, p;
    inline void sync()  while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop(); 
    inline void push(int x)  q.push(x); sync(); 
    inline void pop()  q.pop(); sync(); 
    inline void del(int x)  p.push(x), sync(); 
    inline int top()  sync(); return q.top(); 
    inline bool empty()  sync(); return q.empty(); 
    inline int size()  sync(); return q.size() - p.size(); 
 q1[N], q2[N];

inline int qtop(int x) 
    if (q1[x].empty()) return 0;
    else return q1[x].top();

inline int qtop2(int x) 
    int ans = q2[x].empty() ? 0 : q2[x].top(), sum = 0, tmp;
    if (q1[x].empty()) return ans;
    sum = q1[x].top(), q1[x].pop(), tmp = sum;
    if (!q1[x].empty()) sum += q1[x].top();
    q1[x].push(tmp);
    return std::max(sum, ans);
// 记录一下这里的 sb 错误:最后返回的时候写成了 std::max(sum, tmp) 调了半个上午。

struct Matrix 
    int a[3][3];
    inline Matrix()  memset(a, 0, sizeof(a)); 
    inline Matrix operator * (const Matrix &b) 
        Matrix c;
        c.a[0][0] = std::max(std::max(a[0][0] + b.a[0][0], a[0][1] + b.a[1][0]), a[0][2] + b.a[2][0]);
        c.a[0][1] = std::max(std::max(a[0][0] + b.a[0][1], a[0][1] + b.a[1][1]), a[0][2] + b.a[2][1]);
        c.a[0][2] = std::max(std::max(a[0][0] + b.a[0][2], a[0][1] + b.a[1][2]), a[0][2] + b.a[2][2]);
        c.a[1][0] = std::max(std::max(a[1][0] + b.a[0][0], a[1][1] + b.a[1][0]), a[1][2] + b.a[2][0]);
        c.a[1][1] = std::max(std::max(a[1][0] + b.a[0][1], a[1][1] + b.a[1][1]), a[1][2] + b.a[2][1]);
        c.a[1][2] = std::max(std::max(a[1][0] + b.a[0][2], a[1][1] + b.a[1][2]), a[1][2] + b.a[2][2]);
        c.a[2][0] = std::max(std::max(a[2][0] + b.a[0][0], a[2][1] + b.a[1][0]), a[2][2] + b.a[2][0]);
        c.a[2][1] = std::max(std::max(a[2][0] + b.a[0][1], a[2][1] + b.a[1][1]), a[2][2] + b.a[2][1]);
        c.a[2][2] = std::max(std::max(a[2][0] + b.a[0][2], a[2][1] + b.a[1][2]), a[2][2] + b.a[2][2]);
        return c;
    
 t[N << 2];

inline void dfs1(int x, int fa = 0) 
    dep[x] = dep[fa] + 1, f[x] = fa, siz[x] = 1;
    for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);

inline void dfs2(int x, int pa) 
    top[x] = pa, dfn[x] = ++dfc, pre[dfc] = x;
    if (!son[x]) return (void)(bot[x] = x);
    dfs2(son[x], pa), bot[x] = bot[son[x]];
    for fec(i, x, y) if (y != f[x] && y != son[x]) dfs2(y, y);


inline void build(int o, int L, int R) 
    if (L == R) 
        int x = pre[L];
        if (w[x]) t[o].a[0][0] = 1, t[o].a[0][1] = -INF, t[o].a[0][2] = qtop(x) + 1;
        else t[o].a[0][0] = -INF, t[o].a[0][1] = -INF, t[o].a[0][2] = 0;
        t[o].a[1][0] = qtop(x), t[o].a[1][1] = 0, t[o].a[1][2] = qtop2(x);
        t[o].a[2][0] = -INF, t[o].a[2][1] = -INF, t[o].a[2][2] = 0;
        return;
    
    int M = (L + R) >> 1;
    build(lc, L, M), build(rc, M + 1, R);
    t[o] = t[lc] * t[rc];

inline void qadd(int o, int L, int R, int x) 
    if (L == R) 
        int x = pre[L];
        if (w[x]) t[o].a[0][0] = 1, t[o].a[0][1] = -INF, t[o].a[0][2] = qtop(x) + 1;
        else t[o].a[0][0] = -INF, t[o].a[0][1] = -INF, t[o].a[0][2] = 0;
        t[o].a[1][0] = qtop(x), t[o].a[1][1] = 0, t[o].a[1][2] = qtop2(x);
        t[o].a[2][0] = -INF, t[o].a[2][1] = -INF, t[o].a[2][2] = 0;
        return;
    
    int M = (L + R) >> 1;
    if (x <= M) qadd(lc, L, M, x);
    else qadd(rc, M + 1, R, x);
    t[o] = t[lc] * t[rc];

inline Matrix qsum(int o, int L, int R, int l, int r) 
    if (l <= L && R <= r) return t[o];
    int M = (L + R) >> 1;
    if (r <= M) return qsum(lc, L, M, l, r);
    if (l > M) return qsum(rc, M + 1, R, l, r);
    return qsum(lc, L, M, l, r) * qsum(rc, M + 1, R, l, r);


inline Matrix qry(int x)  return qsum(1, 1, n, dfn[x], dfn[bot[x]]) * Matrix(); 
inline void upd(int x, int k) 
    w[x] = k;
    while (top[x] != 1) 
        const Matrix &tmp1 = qry(top[x]);
        qadd(1, 1, n, dfn[x]);
        const Matrix &tmp2 = qry(top[x]);
        x = f[top[x]];
        q1[x].del(tmp1.a[0][0]), q1[x].push(tmp2.a[0][0]);
        q2[x].del(tmp1.a[1][0]), q2[x].push(tmp2.a[1][0]);
    
    qadd(1, 1, n, dfn[x]);


inline void work() 
    dfs1(1), dfs2(1, 1), build(1, 1, n);
    for (int x = 1; x <= n; ++x)
        for fec(i, x, y) if (f[x] == y) v[g[i].l].pb(x), v[g[i].r].pb(-x);
    for (int i = 1; i <= n; ++i) 
        for (int x : v[i]) if (x > 0) upd(x, 1);
        ans[i] = qry(1).a[1][0];
        for (int x : v[i]) if (x < 0) upd(-x, 0);
    
    int x;
    while (m--) read(x), printf("%d\n", ans[x]);


inline void init() 
    read(n), read(m);
    int x, y, l, r;
    for (int i = 1; i < n; ++i) read(x), read(y), read(l), read(r), adde(x, y, l, r);


int main() 
#ifdef hzhkk
    freopen("hkk.in", "r", stdin);
#else
    File(racing);
#endif
    init();
    work();
    fclose(stdin), fclose(stdout);
    return 0;

附录:线段树分治的做法以及每一个部分分的写法:

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) return a < b ? a = b , 1 : 0;
template<typename A, typename B> inline char smin(A &a, const B &b) return b < a ? a = b , 1 : 0;

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I>
inline void read(I &x) 
    int f = 0, c;
    while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    x = c & 15;
    while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    f ? x = -x : 0;


const int N = 70000 + 7;

int n, m, flag_l1, flag_lian;
int _ask[N];

struct Edge int to, ne, l, r; g[N << 1]; int head[N], tot;
inline void addedge(int x, int y, int l, int r) g[++tot].to = y; g[tot].l = l; g[tot].r = r; g[tot].ne = head[x]; head[x] = tot;
inline void adde(int x, int y, int l, int r) addedge(x, y, l, r); addedge(y, x, l, r);

namespace Task1 
    int v, ans;

    inline void dfs(int x, int dep = 0, int fa = 0) 
        smax(ans, dep);
        for fec(i, x, y) if (y != fa) 
            if (v < g[i].l || v > g[i].r) continue;
            dfs(y, dep + 1, x);
        
    

    inline void work() 
        for (int i = 1; i <= m; ++i) 
            v = _ask[i]; ans = 0;
            for (int j = 1; j <= n; ++j) dfs(j);
            printf("%d\n", ans);
        
    


namespace Task2 
    int ans[N], w[N];

    std::set<pii> s;
    std::vector<int> wph[N];

    inline void work() 
        for (int i = 1; i < n; ++i)
            for fec(j, i, y) if (y == i + 1) w[i] = g[j].r;
        for (int i = 1; i < n; ++i) wph[w[i]].pb(i);
        for (int i = 1; i <= n; ++i) s.insert(pii(i, i));
        for (int i = n; i; --i) 
            ans[i] = ans[i + 1];
            if (wph[i].empty()) continue;
            for (auto j : wph[i]) 
                auto p = s.upper_bound(pii(j, n)), q = p;
                --q;
                smax(ans[i], p->se - q->fi);
                pii neww(q->fi, p->se);
                s.erase(p), s.erase(q);
                s.insert(neww);
            
        
        for (int i = 1; i <= m; ++i) printf("%d\n", ans[_ask[i]]);
    


namespace Task3 
    int ans[N], w[N], v[N];

    std::set<pii> s;
    std::vector<int> wph[N], thx[N];

    struct Heap 
        std::priority_queue<int> q, p;
        inline void sync() while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop();
        inline void push(int x) q.push(x), sync();
        inline void del(int x) p.push(x), sync();
        inline int top() sync(); return q.top();
        inline bool empty() sync(); return q.empty();
     yql;

    inline void work() 
        for (int i = 1; i < n; ++i)
            for fec(j, i, y) if (y == i + 1) w[i] = g[j].r, v[i] = g[j].l;
        for (int i = 1; i < n; ++i) wph[w[i]].pb(i);
        for (int i = 1; i < n; ++i) thx[v[i]].pb(i);
        for (int i = 1; i <= n; ++i) s.insert(pii(i, i)), yql.push(0);
        for (int i = n; i; --i) 
            for (auto j : wph[i]) 
                auto p = s.upper_bound(pii(j, n)), q = p;
                --q;
                pii neww(q->fi, p->se);
                yql.del(q->se - q->fi), yql.del(p->se - p->fi);
                s.erase(p), s.erase(q);
                s.insert(neww);
                yql.push(neww.se - neww.fi);
            
            ans[i] = yql.top();
            // dbg("i = %d, ans[i] = %d\n", i, ans[i]);
            for (auto j : thx[i]) 
                // dbg("-----orz: ----- i = %d, j = %d\n", i, j);
                auto p = s.upper_bound(pii(j, n));
                --p;
                pii new1(p->fi, j), new2(j + 1, p->se);
                yql.del(p->se - p->fi);
                s.erase(p);
                yql.push(new1.se - new1.fi), yql.push(new2.se - new2.fi);
                s.insert(new1), s.insert(new2);
            
        
        for (int i = 1; i <= m; ++i) printf("%d\n", ans[_ask[i]]);
    


namespace Task4 
    #define lc o << 1
    #define rc o << 1 | 1
    
    inline int dist(int, int);
    
    int dfc;
    int dfn[N], dep[N], siz[N], son[N], f[N], top[N];
    int fa[N], mxd[N], ans[N];
    struct wph 
        int x, y, len;
        inline wph() len = 0;
        inline wph(int x, int y) : x(x), y(y) len = dist(x, y);
        inline bool operator < (const wph &a) const return len < a.len;
     val[N];
    
    inline wph merge(wph, wph);
    
    inline int find(int x) return fa[x] == x ? x : find(fa[x]);
    inline void merge(int x, int y) 
        x = find(x), y = find(y);
        if (mxd[x] < mxd[y]) std::swap(x, y);
        fa[y] = x; val[x] = merge(val[x], val[y]);
        smax(mxd[x], mxd[y] + 1);
    
    
    inline void dfs1(int x, int fa = 0) 
        dep[x] = dep[fa] + 1; f[x] = fa; siz[x] = 1;
        for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);
    
    inline void dfs2(int x, int pa) 
        top[x] = pa; dfn[x] = ++dfc;
        if (!son[x]) return; dfs2(son[x], pa);
        for fec(i, x, y) if (y != f[x] && y != son[x]) dfs2(y, y);
    
    inline int LCA(int x, int y) 
        while (top[x] != top[y]) dep[top[x]] > dep[top[y]] ? x = f[top[x]] : y = f[top[y]];
        return dep[x] < dep[y] ? x : y;
    
    inline int dist(int x, int y) return dep[x] + dep[y] - (dep[LCA(x, y)] << 1);
    
    std::vector<pii> t[N << 2], rem[N << 2];
    std::vector<wph> remx[N << 2], remy[N << 2];
    inline void qadd(int o, int L, int R, int l, int r, pii k) 
        if (l <= L && R <= r) return t[o].pb(k);
        int M = (L + R) >> 1;
        if (l <= M) qadd(lc, L, M, l, r, k);
        if (r > M) qadd(rc, M + 1, R, l, r, k);
    
    
    inline wph merge(wph a, wph b) 
        wph ans = std::max(a, b);
        smax(ans, wph(a.x, b.x));
        smax(ans, wph(a.x, b.y));
        smax(ans, wph(a.y, b.x));
        smax(ans, wph(a.y, b.y));
        return ans;
    
    
    inline void build() 
        for (int i = 1; i <= tot; i += 2) 
            int x = g[i].to, y = g[i + 1].to, l = g[i].l, r = g[i].r;
            qadd(1, 1, n, l, r, pii(x, y));
        
    
    
    inline void solve(int o, int L, int R, int ansv) 
        int len = t[o].size();
        for (auto i : t[o]) 
            int x = i.fi, y = i.se;
            rem[o].pb(pii(x = find(x), y = find(y)));
            remx[o].pb(val[x]), remy[o].pb(val[y]);
            merge(x, y); smax(ansv, std::max(val[x].len, val[y].len));
        
        if (L == R) ans[L] = ansv;
        else 
            int M = (L + R) >> 1;
            solve(lc, L, M, ansv), solve(rc, M + 1, R, ansv);
        
        for (int ii = len - 1; ~ii; --ii) 
            auto i = t[o][ii];
            int x  = i.fi, y = i.se;
            x = rem[o][ii].fi, y = rem[o][ii].se; rem[o].pop_back();
            fa[x] = x, fa[y] = y;
            val[x] = remx[o][ii], val[y] = remy[o][ii];
            remx[o].pop_back(), remy[o].pop_back();
        
    
    
    inline void ycl() 
        for (int i = 1; i <= n; ++i) 
            fa[i] = i;
            val[i] = wph(i, i);
        
    
    
    inline void work() 
        build();
        dfs1(1), dfs2(1, 1);
        ycl();
        solve(1, 1, n, 0);
        for (int i = 1; i <= m; ++i) printf("%d\n", ans[_ask[i]]);
    


inline void init() 
    int u, v, l, r;
    read(n), read(m);
    flag_l1 = flag_lian = 1;
    for (int i = 1; i < n; ++i) 
        read(u), read(v), read(l), read(r);
        adde(u, v, l, r);
        flag_l1 = flag_l1 && l == 1;
        flag_lian = flag_lian && u == i && v == i + 1;
    
    for (int i = 1; i <= m; ++i) read(_ask[i]);


int main() 
    #ifdef hzhkk
    freopen("hkk.in", "r", stdin);
    #else
    File(racing);
    #endif
    init();
    if (flag_l1 && flag_lian) Task2::work();
    else if (flag_lian) Task3::work();
    else if (n <= 20 && m <= 20) Task1::work();
    else Task4::work();
    fclose(stdin), fclose(stdout);
    return 0;

以上是关于「校内训练 2019-04-23」越野赛车问题 动态dp+树的直径的主要内容,如果未能解决你的问题,请参考以下文章

越野赛车问题

dtoj#4259. 越野赛车问题

彩虹越野无人车竞赛

DARPA 的 RACER 计划全面推进越野高速自动驾驶汽车

贪心策略USACO 越野跑

fzyzojP3372 -- [校内训练20171124]博弈问题