zoukankan      html  css  js  c++  java
  • [学习笔记]动态动态规划/动态DP/DDP

    概述

    (DDP)是指一类需要支持修改的(DP)问题,常见的主要是带修树形(DP),可以用树链剖分结合矩阵乘法优化修改的复杂度

    详解

    从例题来分析:洛谷P4719

    题目大意:给出(n)个点的树,每个点有点权,共(m)次操作,每次修改一个点的点权,求每次修改后树的最大权独立集的权值大小

    (n, m le 1e5)


    如果不带修改,容易想到设(f_{u, 0/1})来表示以(u)为根的子树,选/不选(u)的答案,推出转移:

    [egin{align} f_{u, 0} & = sum_{v in son_u} max(f_{v, 0}, f_{v, 1}) \ f_{u, 1} & = w_u + sum_{v in son_u} f_{v, 0} end{align} ]

    答案就是(max (f_{1, 0}, f_{1, 1}))

    但是这样单次修改需要修改到根的路径上的所有点,是(O(n))

    既然修改的是一条路径,不妨试试树链剖分

    但是按上面的方式转移显然不能将一条链上的转移合并,所以考虑重新设计一下状态,(f)的含义不变,增加一个(g_{u, 0/1})表示只考虑(u)的轻子树的答案,那么就有:

    [egin{align} g_{u, 0} & = sum_{v in lson_u} max(f_{v, 0}, f_{v, 1}) \ g_{u, 1} & = w_u + sum_{v in lson_u} f_{v, 0} \ f_{u, 0} & = g_{u, 0} + max(f_{hv[u], 0}, f_{hv[u], 1}) \ f_{u, 1} & = g_{u, 1} + f_{hv[u], 0} end{align} ]

    观察后两式中(f)(g)的关系我们发现貌似可以写成形如矩阵乘法的形式,如果我们重新定义矩阵乘法为(C_{i, j} = max_{k = 1}^{n} (A_{i, k} + B_{k, j})),就会有:

    [left[ egin{matrix} f_{hv[u], 0} & f_{hv[u], 1} end{matrix} ight] * left[ egin{matrix} g_{u, 0} & g_{u, 1} \ g_{u, 0} & -infty end{matrix} ight] = left[ egin{matrix} f_{u, 0} & f_{u, 1} end{matrix} ight] ]

    容易证明新定义的矩阵乘法具有结合律,那么就可以树链剖分后用线段树维护重链上第二个矩阵的积

    于是我们可以发现:

    1. 如果一个点是链顶,它的(f)会对它父亲的(g)产生贡献
    2. 否则,它的(g)会对它所在链的链顶产生贡献,且已经统计进线段树中

    所以我们只需要线段树维护(g)的积,同时维护下每条链链顶的(f)就行了

    具体做法就是先在线段树中更新当前节点的(g),然后更新当前链顶的(f),然后跳到链顶的父亲,重复这个过程就可以了,更具体的可见代码的(modify)函数

    复杂度(O(n log^2 n))

    一个细节:矩阵乘法不具有交换律,注意是从下往上乘,按(dfs)序从大到小乘

    PS.此题还有(O(n log n))(LCT)做法

    代码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #define MAXN 100005
    
    typedef long long LL;
    const LL INF = 0x3f3f3f3f3f3f3f3f;
    struct Matrix {
    	LL data[2][2];
    	Matrix() { memset(data, 0, sizeof data); }
    	Matrix(LL a00, LL a01, LL a10, LL a11) { data[0][0] = a00, data[0][1] = a01, data[1][0] = a10, data[1][1] = a11; }
    	static Matrix indentity() { return Matrix(1, 0, 0, 1); }
    	Matrix operator *(const Matrix &) const;
    };
    struct SegmentTree {
    	Matrix data[MAXN << 2];
    	void modify(int, int, int, int, const Matrix &);
    	void query(int, int, int, int, int, Matrix &);
    };
    
    char gc();
    int read();
    void dfs1(int);
    void dfs2(int);
    void modify(int, int);
    
    int N, M, val[MAXN];
    int idx, top[MAXN], bot[MAXN], dep[MAXN], fa[MAXN], dfn[MAXN], size[MAXN], heavy[MAXN];
    LL f[MAXN][2], g[MAXN][2];
    std::vector<int> trans[MAXN];
    SegmentTree sgt;
    
    int main() {
    	//freopen("tmp.in", "r", stdin);
    	//freopen("tmp.out", "w", stdout);
    	
    	N = read(), M = read();
    	for (int i = 1; i <= N; ++i) val[i] = read();
    	for (int i = 1; i < N; ++i) {
    		int u = read(), v = read();
    		trans[u].push_back(v);
    		trans[v].push_back(u);
    	}
    	dfs1(1);
    	top[1] = 1;
    	dfs2(1);
    	while (M--) {
    		int x = read(), y = read();
    		modify(x, y);
    		printf("%lld
    ", std::max(f[1][0], f[1][1]));
    	}
    	return 0;
    }
    inline char gc() {
    	static char buf[1000000], *p1, *p2;
    	if (p1 == p2) p1 = (p2 = buf) + fread(buf, 1, 1000000, stdin);
    	return p1 == p2 ? EOF : *p2++;
    }
    inline int read() {
    	int res = 0, op; char ch = gc();
    	while (ch != '-' && (ch < '0' || ch > '9')) ch = gc();
    	op = (ch == '-' ? ch = gc(), -1 : 1);
    	while (ch >= '0' && ch <= '9') res = (res << 1) + (res << 3) + ch - '0', ch = gc();
    	return res * op;
    }
    void dfs1(int u) {
    	dep[u] = dep[fa[u]] + 1;
    	size[u] = 1;
    	for (int i = 0; i < trans[u].size(); ++i) {
    		int v = trans[u][i];
    		if (v ^ fa[u]) {
    			fa[v] = u, dfs1(v);
    			size[u] += size[v];
    			if (!heavy[u] || size[v] > size[heavy[u]]) heavy[u] = v;
    		}
    	}
    }
    void dfs2(int u) {
    	dfn[u] = ++idx;
    	g[u][0] = 0, g[u][1] = val[u];
    	if (heavy[u]) {
    		top[heavy[u]] = top[u];
    		dfs2(heavy[u]);
    		bot[u] = bot[heavy[u]];
    	} else bot[u] = u;
    	for (int i = 0; i < trans[u].size(); ++i) {
    		int v = trans[u][i];
    		if (v == fa[u] || v == heavy[u]) continue;
    		top[v] = v, dfs2(v);
    		g[u][0] += std::max(f[v][0], f[v][1]);
    		g[u][1] += f[v][0];
    	}
    	f[u][0] = g[u][0] + std::max(f[heavy[u]][0], f[heavy[u]][1]);
    	f[u][1] = g[u][1] + f[heavy[u]][0];
    	sgt.modify(1, 1, N, dfn[u], Matrix(g[u][0], g[u][1], g[u][0], -INF));
    }
    Matrix Matrix::operator *(const Matrix &m) const {
    	Matrix res;
    	res.data[0][0] = std::max(data[0][0] + m.data[0][0], data[0][1] + m.data[1][0]);
    	res.data[0][1] = std::max(data[0][0] + m.data[0][1], data[0][1] + m.data[1][1]);
    	res.data[1][0] = std::max(data[1][0] + m.data[0][0], data[1][1] + m.data[1][0]);
    	res.data[1][1] = std::max(data[1][0] + m.data[0][1], data[1][1] + m.data[1][1]);
    	return res;
    }
    void SegmentTree::modify(int rt, int L, int R, int pos, const Matrix &m) {
    	if (L == R) data[rt] = m;
    	else {
    		int mid = (L + R) >> 1;
    		if (pos <= mid) modify(rt << 1, L, mid, pos, m);
    		else modify(rt << 1 | 1, mid + 1, R, pos, m);
    		data[rt] = data[rt << 1 | 1] * data[rt << 1];//注意乘的顺序 
    	}
    }
    void SegmentTree::query(int rt, int L, int R, int l, int r, Matrix &res) {
    	if (L >= l && R <= r) res = res * data[rt];//注意乘的顺序 
    	else {
    		int mid = (L + R) >> 1;
    		if (r > mid) query(rt << 1 | 1, mid + 1, R, l, r, res);
    		if (l <= mid) query(rt << 1, L, mid, l, r, res);
    	}
    }
    void modify(int x, int y) {
    	g[x][1] = g[x][1] - val[x] + y;
    	val[x] = y;
    	while (x) {
    		int t = top[x], b = bot[x];
    		sgt.modify(1, 1, N, dfn[x], Matrix(g[x][0], g[x][1], g[x][0], -INF));//在线段树中修改g 
    		Matrix tmp;
    		sgt.query(1, 1, N, dfn[t], dfn[b], tmp);//查出链顶f的新值 
    		g[fa[t]][0] -= std::max(f[t][0], f[t][1]);//更新链顶父亲的g 
    		g[fa[t]][1] -= f[t][0];
    		f[t][0] = tmp.data[0][0], f[t][1] = tmp.data[0][1];//更新链顶 
    		g[fa[t]][0] += std::max(f[t][0], f[t][1]);
    		g[fa[t]][1] += f[t][0];
    		x = fa[t];//跳到链顶的父亲 
    	}
    }
    //Rhein_E
    
  • 相关阅读:
    《高等应用数学问题的MATLAB求解》——第4章习题代码
    《高等应用数学问题的MATLAB求解》——第3章习题代码
    《高等应用数学问题的MATLAB求解》——第2章习题代码
    2020年高考数学全国一卷第16题
    LR&PCA&KPCA
    package.json.lock
    Charles 抓 iphone 手机包
    竞赛196
    竞赛197
    js编程语言!!!!
  • 原文地址:https://www.cnblogs.com/Rhein-E/p/10670464.html
Copyright © 2011-2022 走看看