首页 > 其他分享 >动态 DP

动态 DP

时间:2024-01-22 22:57:57浏览次数:35  
标签:End int max 节点 init 动态 id DP

序列——线段树维护矩阵转移

首先,我们需要普通 DP 方程改为矩阵乘法或广义矩阵乘法形式,可能会增加一些状态,然后用线段树维护矩阵乘法即可。

这个模型推荐使用横向量乘方阵的形式,可以直接从左往右乘。

树——重链剖分

原理

首先,记录每个节点的轻儿子对这个节点 DP 数组的贡献,然后用线段树维护一条重链。

对于一条重链,链底(一定是叶子节点)的矩阵一般是单位阵,其它节点的矩阵表示一个状态转移,初始的向量是一个关于链底的向量,用这个向量乘一个后缀之后,得到这个后缀开头节点的完整 DP 信息。

对于修改的节点,需要修改的有:

  • 这个节点。
  • 到根节点的轻边,轻边上父节点。

实现细节

修改时不可能直接求节点轻儿子对它的贡献,所以考虑增量,这就要求维护前后的两个 DP 信息,具体如下:

  • 求当前链顶的信息。
  • 进行上一个修改(当前节点的修改)。
  • 求当前链顶信息。
  • 用增量改变链顶的父节点的信息。
  • 当前节点条到链顶的父节点。
  • 当链顶节点为根,修改当前节点。

建议使用方阵乘列向量的方式,线段树维护时可以从左往右乘,但查询时要先查询右儿子,再查询左儿子

卡常

  • 对每条重链开线段树。
  • 矩阵乘法循环展开,使用数组存矩阵,但注意 memset 的空间需要是一个具体矩阵的空间(而不是传的指针)。
  • inline,快读。

参考代码(P4751

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5, INF = 1e9;

int n, m, x[N];
int la[N], ne[N * 2], en[N * 2], idx;
int fa[N], dep[N], size[N], son[N], top[N], End[N], id[N], rnk[N], dfst;
int g[N][2], f[N][2];
struct node{
	int l, r;
	int x[2][2];
}t[N * 4];
int root[N], pid;

inline void init(int x[][2])
{
	x[0][0] = x[1][1] = 0;
	x[0][1] = x[1][0] = -INF;
}
inline void init(int x[][2], int g[])
{
	x[0][0] = x[0][1] = g[0];
	x[1][0] = g[1], x[1][1] = -INF;
}
inline void init(int p[], int u)
{
	p[0] = 0, p[1] = x[u];
}
inline void mul(int a[][2], int b[][2], int c[][2])
{
	static int t[2][2];
	t[0][0] = max(a[0][0] + b[0][0], a[0][1] + b[1][0]);
	t[0][1] = max(a[0][0] + b[0][1], a[0][1] + b[1][1]);
	t[1][0] = max(a[1][0] + b[0][0], a[1][1] + b[1][0]);
	t[1][1] = max(a[1][0] + b[0][1], a[1][1] + b[1][1]);
	c[0][0] = t[0][0];
	c[0][1] = t[0][1];
	c[1][0] = t[1][0];
	c[1][1] = t[1][1];
}
inline void mul(int a[][2], int b[], int c[])
{
	static int t[2];
	t[0] = max(a[0][0] + b[0], a[0][1] + b[1]);
	t[1] = max(a[1][0] + b[0], a[1][1] + b[1]);
	c[0] = t[0];
	c[1] = t[1];
}

void build(int &u, int a, int b, int eid)
{
	u = ++ pid;
	if(a == b) return a == eid ? init(t[u].x) : init(t[u].x, g[rnk[a]]);
	int mid = a + b >> 1;
	build(t[u].l, a, mid, eid), build(t[u].r, mid + 1, b, eid);
	mul(t[t[u].l].x, t[t[u].r].x, t[u].x);
}
void modify(int u, int x, int g[], int a, int b, int eid)
{
	if(a == b) return a == eid ? init(t[u].x) : init(t[u].x, g);
	int mid = a + b >> 1;
	if(x <= mid) modify(t[u].l, x, g, a, mid, eid);
	else modify(t[u].r, x, g, mid + 1, b, eid);
	mul(t[t[u].l].x, t[t[u].r].x, t[u].x);
}
inline void query(int u, int g[])
{
	mul(t[u].x, g, g);
}

inline void add(int a, int b)
{
	ne[ ++ idx] = la[a];
	la[a] = idx;
	en[idx] = b;
}
void dfs1(int u, int f)
{
	fa[u] = f, dep[u] = dep[f] + 1, size[u] = 1;
	for(int i = la[u]; i; i = ne[i])
	{
		int v = en[i];
		if(v != f)
		{
			dfs1(v, u);
			size[u] += size[v];
			if(size[v] > size[son[u]]) son[u] = v;
		}
	}
}
void dfs2(int u, int ft)
{
	top[u] = ft;
	rnk[id[u] = ++ dfst] = u;
	if(son[u])
	{
		dfs2(son[u], ft), End[u] = End[son[u]];
		g[u][0] = 0, g[u][1] = x[u];
		for(int i = la[u]; i; i = ne[i])
		{
			int v = en[i];
			if(v != fa[u] && v != son[u])
			{
				dfs2(v, v);
				g[u][0] += max(f[v][0], f[v][1]);
				g[u][1] += f[v][0];
			}
		}
		f[u][0] = g[u][0] + max(f[son[u]][0], f[son[u]][1]);
		f[u][1] = g[u][1] + f[son[u]][0];
	}
	else End[u] = u, g[u][1] = f[u][1] = x[u];
	if(u == ft) build(root[u], id[u], id[End[u]], id[End[u]]);
}

inline int modify(int u, int y)
{
	static int p[2][2], f1[2], f2[2];
	g[u][1] += y - x[u];
	init(p, g[u]);
	int t = 1;
	while(top[u] != 1)
	{
		int v = top[u], w = fa[v];
		init(f1, End[u]);
		query(root[v], f1);
		if(t) x[u] = y, t = 0;
		modify(root[v], id[u], g[u], id[v], id[End[v]], id[End[v]]);
		init(f2, End[u]);
		query(root[v], f2);
		g[w][0] += max(f2[0], f2[1]) - max(f1[0], f1[1]);
		g[w][1] += f2[0] - f1[0];
		u = w;
	}
	if(t) x[u] = y;
	modify(root[1], id[u], g[u], id[1], id[End[1]], id[End[1]]);
	init(f1, End[1]);
	query(root[1], f1);
	return max(f1[0], f1[1]);
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++ ) scanf("%d", &x[i]);
	for(int i = 1; i < n; i ++ )
	{
		int a, b;
		scanf("%d%d", &a, &b);
		add(a, b), add(b, a);
	}
	dfs1(1, 0), dfs2(1, 1);
	int ans = 0;
	while(m -- )
	{
		int x, y;
		scanf("%d%d", &x, &y);
		x ^= ans;
		printf("%d\n", ans = modify(x, y));
	}
	return 0;
}

标签:End,int,max,节点,init,动态,id,DP
From: https://www.cnblogs.com/recollect-the-past/p/17981308

相关文章

  • 【动态规划】正则表达式
    目录1.题目2.应用2.1.Leetcode10.正则表达式匹配题目解题思路代码实现1.题目题目列表:序号题目难度110.正则表达式匹配困难2.应用2.1.Leetcode10.正则表达式匹配题目10.正则表达式匹配解题思路设\(dp[i][j]\)表示\(s\)的前\(i\)个字符与......
  • 深入分析若依数据权限@datascope (注解+AOP+动态sql拼接) 【循序渐进,附分析过程】
    除了我们平时都知道的路由权限(即对接口的访问权限外),在日常生产开发中,我们还应该有对数据的访问权限。在若依这个框架中,通过角色中的数据范围这个属性来对数据权限进行控制。对应实体类:深入分析一个用户肯定是有一种角色的,也肯定是隶属于一个部门的。这里咱们就以用户在......
  • 小景的Dba之路--impdp导入数据问题报错排查总结
    小景最近在工作中遇到了一个问题,用impdp做数据导入的时候,有以下报错,下面是问题排查过程:首先看到了ORA-01950:noprivilegesontablespace‘PUBDATA’这个报错,小景想到了以下原因:权限问题:ORA-01950错误表示用户没有在PUBDATA表空间上的特定对象的权限。这可能是由于数据库权......
  • CF-461-B-树形DP
    461-B题目大意给定一棵\(n\)个节点的树,节点编号从\(0\)开始,每个节点要么为白色要么为黑色,你需要删除一些边,使得剩下的各个连通块中有且仅有一个黑色节点。问有多少种删边方案数,答案对\(10^9+1\)取模。Solution考虑树形DP,令\(dp[x][0/1]\)表示节点\(x\)属于无黑色节点/有黑色......
  • 动态规划(9) 编辑距离最终版
    目录115不同的子序列583.两个字符串的删除操作编辑距离115不同的子序列dp数组的含义:dp[i][j]以i-1结尾的s中包含有多少个以j-1为结尾的tdp初始化:dp[0][0]空字符串中有多少个空字符串,显然1个dp[i][0]以i-1为结尾的s中包含有多少个空字符串,也是1个递推公式:显然需要考虑s[i......
  • 动态规划(8) 编辑距离
    目录1035不相交的线53最大子数组和392判断子序列1035不相交的线弄清楚在求什么,实际上是在求最长公共子序列classSolution{public:intmaxUncrossedLines(vector<int>&nums1,vector<int>&nums2){intn=nums1.size();intm=nums2.size();......
  • 动态规划- leecode 122
    Problem:122.买卖股票的最佳时机II目录思路解题方法复杂度Code思路仍然是一道比较简单的动态规划,但是一上手做还是没理清楚状态是什么。一天的状态只有两种,持有股票和没有股票,这样就可以列出状态转移方程\(dp[i][j]=max(dp[i-1][j],dp[i-1][j*]+或-price[i])\),这里的j表......
  • HDP 相关日志位置
      1.HIVESERVER2的日志:/var/log/hive-rwxrwxrwx1hivehadoop4791月1418:20hive.err-rwxrwxrwx1hivehadoop24381月1320:27hivemetastore-gc-2024-01-13_20-27-25.log.0.current-rwxrwxrwx1hivehadoop1032641月1321:47hivemetastor......
  • 数位 dp
    前言带有私人情感,请理性阅读。直接跳到理论去算了。前言不同于其他dp,数位dp的初学者很容易懵逼,比如我。都是递推版数位dp害的!看到oj上“简单”的模板题,题解中生动抽象的分析——什么顶着上界枚举、分开讨论、处理前导\(0\)、满\(i\)位时记为\(dp_i\)、统计答案......
  • 线性DP简单总结
    线性DP简单总结动态规划原理可以用动态规划解决的问题,应具备三种条件:最优子结构(由小推大)无后效性(由过去推现在)子问题重叠(已经求解的子问题不必重复求解)动态规划构成“状态”“阶段”“决策”为动态规划三要素。最长上升子序列问题给定一个长度为\(n\)的序列\(A......