算法思想
把树上dp的过程看作是结点
u
u
<script type="math/tex" id="MathJax-Element-4">u</script>根据自身的数据对儿子传上来的dp值进行加工(变换),进一步表示为根据自身数据和轻儿子的dp值对重儿子传上来的dp值进行变换。
要满足变换是可合并的。例如矩阵(线性变换)、加乘max混合运算等。
然后用一个线段树维护一条链的和变换。
需要维护对轻儿子的dp值求并的操作。如果是取max可以对每个父亲开一个可删除堆来维护。
修改操作
- 单点修改并在链的线段树上一路
pushup
- 从树根读取答案(即将此变换施加于空状态上)
- 把这个答案
obtain
到链顶的父亲top
所属的堆 - 尝试用堆更新
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
}