ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

BZOJ4919 大根堆(树形dp+线段树合并)

2022-06-02 22:31:35  阅读:142  来源: 互联网

标签:大根堆 val BZOJ4919 nd sgt mid int dp


multiset 启发式合并贪心维护 LIS 的做法就不多说了,网上题解一大堆,着重讲一下线段树合并维护 \(dp\)。

\(O(n^2)\) 的 \(dp\) 非常显然。离散化后,设 \(dp[u][i]\) 表示节点 \(u\) 的子树中,最大值为 \(i\) 时最多取多少个节点。转移时考虑是否将节点 \(u\) 加入大根堆并分类讨论。

这样的状态不支持快速合并。考虑优化状态,设 \(dp[u][i]\) 表示节点 \(u\) 的子树中,最大值 \(\le i\) 时最多取多少个节点,并尝试使用线段树合并来维护。

转移同样考虑两种情况。如果 \(u\) 不取,那么直接 \(dp[u][i] = \sum dp[v][i]\) 即可,将 \(u\) 的儿子的线段树合并。合并之后,如果 \(u\) 取,就要求子树中取的最大值都 \(\lt val[u]\),那么要用 \(\max\limits_{i \lt val[u]}dp[u][i]+1\) 去更新 \(dp[u][\ge val[u]]\)。

注意到,根据状态的设计,\(dp[u]\) 其实是一个单调不减的序列,所以 \(\max\limits_{i \lt val[u]} dp[u][i]+1\) 其实相当于 \(dp[u][val[u]-1]+1\)。同时由于 \(dp[u]\) 单调不减,所以 \(dp[u][\ge val[u]]\) 中会被更新的值是一段左端点为 \(val[u]\) 的区间,这段区间的值都是 \(dp[u][val[u]-1]\)。

于是,可以二分出区间的右端点。具体地,二分找到最后一个 \(i\) 使得 \(dp[u][i] \lt dp[u][val[u]-1]+1\),然后在线段树上将区间 \([val[u],i]\) 覆盖或直接 \(+1\) 即可,需要标记永久化。

综上所述,该算法的时间复杂度为 \(O(n \log^2 n)\)。

/**
 * @file:           BZOJ4919.cpp
 * @author:         yaoxi-std
 * @url:            
*/
#pragma GCC optimize ("O2")
#pragma GCC optimize ("Ofast", "inline", "-ffast-math")
#pragma GCC target ("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
#define resetIO(x) \
    freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout)
#define debug(fmt, ...) \
    fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
template <class _Tp>
inline _Tp& read(_Tp& x) {
    bool sign = false; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) sign |= (ch == '-');
    for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + (ch ^ 48);
    return sign ? (x = -x) : x;
}
template <class _Tp>
inline void write(_Tp x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x / 10);
    putchar((x % 10) ^ 48);
}
bool m_be;
using ll = long long;
const int MAXN = 2e5 + 10;
const int INF = 0x3f3f3f3f;
int n, m, a[MAXN], fa[MAXN], val[MAXN];
vector<int> g[MAXN];
struct SegmentTree {
    struct Node {
        int ls, rs, sum;
    } nd[MAXN * 25];
    int tot, rt[MAXN];
    int& operator[](int i) { return rt[i]; }
    void update(int& i, int l, int r, int ql, int qr, int v) {
        if (ql > qr) return;
        if (!i) i = ++tot;
        if (ql <= l && r <= qr) return void(nd[i].sum += v);
        int mid = (l + r) >> 1;
        if (ql <= mid) update(nd[i].ls, l, mid, ql, qr, v);
        if (qr > mid) update(nd[i].rs, mid + 1, r, ql, qr, v);
    }
    int query(int i, int l, int r, int p) {
        if (!i || !p) return 0;
        if (l == r) return nd[i].sum;
        int mid = (l + r) >> 1;
        if (p <= mid) return nd[i].sum + query(nd[i].ls, l, mid, p);
        return nd[i].sum + query(nd[i].rs, mid + 1, r, p);
    }
    void merge(int& x, int y, int l, int r) {
        if (!x || !y) return void(x = x | y);
        if (l == r) return void(nd[x].sum += nd[y].sum);
        int mid = (l + r) >> 1;
        merge(nd[x].ls, nd[y].ls, l, mid);
        merge(nd[x].rs, nd[y].rs, mid + 1, r);
        nd[x].sum += nd[y].sum;
    }
} sgt;
void dfs(int u) {
    for (auto v : g[u]) dfs(v), sgt.merge(sgt[u], sgt[v], 1, m);
    int tmp = sgt.query(sgt[u], 1, m, a[u] - 1) + 1;
    int l = a[u], r = m, pos = a[u] - 1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (sgt.query(sgt[u], 1, m, mid) < tmp)
            l = mid + 1, pos = mid;
        else
            r = mid - 1;
    }
    sgt.update(sgt[u], 1, m, a[u], pos, 1);
}
bool m_ed;
signed main() {
    read(n);
    for (int i = 1; i <= n; ++i)
        read(a[i]), read(fa[i]), val[i] = a[i], g[fa[i]].push_back(i);
    sort(val + 1, val + n + 1), m = unique(val + 1, val + n + 1) - val - 1;
    for (int i = 1; i <= n; ++i) a[i] = lower_bound(val + 1, val + m + 1, a[i]) - val;
    dfs(1), write(sgt.query(1, 1, m, m)), putchar('\n');
    return 0;
}

标签:大根堆,val,BZOJ4919,nd,sgt,mid,int,dp
来源: https://www.cnblogs.com/yaoxi-std/p/16339197.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有