树链剖分维护DP

算法思想

把树上dp的过程看作是结点 u u <script type="math/tex" id="MathJax-Element-4">u</script>根据自身的数据对儿子传上来的dp值进行加工(变换),进一步表示为根据自身数据和轻儿子的dp值对重儿子传上来的dp值进行变换。
要满足变换是可合并的。例如矩阵(线性变换)、加乘max混合运算等。
然后用一个线段树维护一条链的和变换。
需要维护对轻儿子的dp值求并的操作。如果是取max可以对每个父亲开一个可删除堆来维护。

修改操作

  1. 单点修改并在链的线段树上一路pushup
  2. 从树根读取答案(即将此变换施加于空状态上)
  3. 把这个答案obtain到链顶的父亲top所属的堆
  4. 尝试用堆更新top的数据,如果成功就对top递归调用

注意①所谓“单点修改”包括来自读入的修改和来自④的修改两种。

初始建树

  • dfs时把点的编号往一个数组里存,到叶结点时建出这条链。将点编号从数组中倒序取出,因为一条链上通常要把深的点放在左边,浅的放右边。
  • 每个链分别建线段树要比整个建一棵线段树常数小很多,因为可以改区间查询为全局查询。
  • 鉴于正常的题目都是单点修改,写zkw线段树更方便。

模板代码

struct Heap hp[MAXN];   //removable heap

struct Node {
    void clear();   //empty node (as 0)
    void init();    //unit node (as 1)
    void setup(int w);  //first initialise from w
    void read();        //reinitialise from input
    bool update(Heap &hp);  //get son-data from heap
    void make();    //make son-data into operation tag
    void obtain(Heap &hp);  //push to heap
    void revoke(Heap &hp);  //revoke from heap
    void pushup(Node &g, Node &h);  //merge operation tag
} pool[MAX*3], *pt = pool;

int id[MAX], m = 0;
struct Chain *chn[MAX], *seq[MAX], **tp = seq;
Node *dat[MAX];
int idx[MAX];

struct Chain {
    Node *tr; int top, sz;
    Chain(): tr(pt), top(fa[id[0]]) {   //setup a Chain
        *tp++ = this;   //push to heap to build later in reversed order
        int N = 2;  //prepare seg-tree
        while (N < m) N <<= 1;
        pt += sz = N + m;   //apply mem from pool
        for (int i = N; m; i++) {
            int x = id[--m];    //get data into tree
            tr[i].setup(w[x]);  //in REVERSED order
            chn[x] = this;
            dat[x] = tr + i;//recorde pointers & index
            idx[x] = i;     //convinience for Update
        }
        for (int i = sz+1>>1; i < N; i++)
            tr[i].init();   //set unit node
        pt->init();
    }
    void Build() {
        for (int i = sz-1>>1; i; i--)   //build tree
            tr[i].pushup(tr[i<<1], tr[i<<1|1]);
        if (top) {  //update to ancient Chain
            tr[1].obtain(hp[top]);
            dat[top]->update(hp[top]);
        }
    }
    void Update(int x) {    //pushup x, which was already updated
        if (top) tr[1].revoke(hp[top]);
        while (x >>= 1)
            tr[x].pushup(tr[x<<1], tr[x<<1|1]);
        if (top) {
            tr[1].obtain(hp[top]);
            if (dat[top]->update(hp[top]))
                chn[top]->Update(idx[top]);
        }
    }
};

//dfs1 ignored
void dfs2(int x) {
    id[m++] = x;
    if (son[x]) {
        dfs2(son[x]);
        for (int u : e[x])
            if (u ^ son[x] && u ^ fa[x])
                dfs2(u);
    }
    else new Chain();   //finish a Chain
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值