首页 > 其他分享 >动态 DP 学习笔记

动态 DP 学习笔记

时间:2024-01-30 14:49:08浏览次数:32  
标签:mat int max 笔记 son bmatrix ans 动态 DP

动态 DP

P4719 动态 DP

给定一棵 \(n\) (\(n \leqslant 10^5\)) 个点的树,点带点权。

有 \(m\) (\(m \leqslant 10^5\)) 次操作,每次操作给定 \(x,y\),表示修改点 \(x\) 的权值为 \(y\)。

你需要在每次操作之后求出这棵树的最大权独立集的权值大小。

首先考虑 \(m=0\) 时的做法,可以简单地设计出 dp 转移方程式 \(f_{x, 0 / 1}\) 表示 \(x\) 选或不选的答案。

\[\begin{cases} f_{x, 0} = \sum\limits_{v \in x.\text{son}} \max\{f_{v, 1}, f_{v, 0}\} \\ f_{x, 1} = p_x + \sum\limits_{v \in x.\text{son}} f_{v, 0} \end{cases} \]

考虑用矩阵乘法维护这个东西,这个东西显然不能写成矩阵乘法的形式,故考虑广义矩阵乘法。

广义矩阵乘法

比如此题中的矩阵惩罚可以长这个样子:

\[C_{i, j} = \max\limits_{k = 0}^{size - 1} \left(A_{i, k} + B_{k, j}\right) \]

可以发现,就是将普通矩阵乘法中的加换成了 \(\max\),乘法换成了加法。

对应到上面的递推式,可以添加辅助数组 \(g\),\(g_{x, 0 / 1}\) 表示不考虑 \(x\) 的实儿子的情况下,选或不选 \(x\) 的答案:

\[\begin{cases} g_{x, 0} = \sum\limits_{v \in x.\text{son} \land v \ne son_x} \max\{f_{v, 0}, f_{v, 1}\}\\ g_{x, 1} = p_x + \sum\limits_{v \in x.\text{son} \land v \ne son_x} f_{v, 0} \\ f_{x, 0} = g_{x, 0} + \max\{f_{son_x, 0}, f_{son_x, 1}\}\\ f_{x, 1} = g_{x, 1} + f_{son_x, 0} \end{cases}\\ \begin{bmatrix} g_{x, 0} & g_{x, 0}\\ g_{x, 1} & -\infty \end{bmatrix} \times \begin{bmatrix} f_{son_x, 0} \\ f_{son_x, 1} \end{bmatrix} = \begin{bmatrix} f_{x, 0} \\ f_{x, 1} \end{bmatrix} \]

然后下面的这坨东西是可以用线段树维护的,接着就可以树剖了。

代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define int i64
const int N = 1E5 + 5;
const int inf = 0x3f3f3f3f;
int n, m, p[N];
vector <int> G[N];
int sz[N], son[N], top[N], dfn[N], tot, fa[N], dep[N], wt[N];
int f[N][2], g[N][2], epos[N];
void dfs1(int x) {
  sz[x] = 1; pair <int, int> mx = make_pair(0, 0);
  f[x][1] = p[x]; f[x][0] = 0;
  for (auto v : G[x]) {
    if (v == fa[x]) continue;
    fa[v] = x; dep[v] = dep[x] + 1;
    dfs1(v); sz[x] += sz[v];
    mx = max(mx, make_pair(sz[v], v));
    f[x][0] += max(f[v][0], f[v][1]);
    f[x][1] += f[v][0];
  } son[x] = mx.second;
}
void dfs2(int x, int topf) {
  top[x] = topf; wt[dfn[x] = ++tot] = x;
  g[x][1] = p[x];
  if (!son[x]) return void(epos[topf] = tot);
  dfs2(son[x], topf);
  for (auto v : G[x]) {
    if (v == son[x] || v == fa[x]) continue;
    dfs2(v, v);
    g[x][0] += max(f[v][0], f[v][1]);
    g[x][1] += f[v][0];
  }
}
struct mat {
  int a[2][2];
  mat () {
    a[0][0] = a[1][0] = 
    a[0][1] = a[1][1] = 0;
  }
  void set(int x, int y, int val = 1) {a[x][y] = val;}
} ;
mat bp(int x) {
  mat ans; ans.set(1, 1, -inf);
  ans.set(0, 0, g[x][0]); ans.set(0, 1, g[x][0]);
  ans.set(1, 0, g[x][1]); return ans;
}
mat mul(mat x, mat y) {
  mat ans; 
  for (int i = 0; i < 2; ++i) for (int j = 0; j < 2; ++j)
    for (int k = 0; k < 2; ++k)
      ans.a[i][j] = max(ans.a[i][j], x.a[i][k] + y.a[k][j]);
  return ans;
}
struct segt {
  struct node {
    int l, r;
    mat p;
  } t[N << 2];
  int lson(int x) {return x << 1;}
  int rson(int x) {return x << 1 | 1;}
  void pushup(int x) {t[x].p = mul(t[lson(x)].p, t[rson(x)].p);}
  void build(int x, int l, int r) {
    t[x].l = l; t[x].r = r;
    if (l == r) {
      t[x].p = bp(wt[l]);
      return ;
    } int mid = (l + r) >> 1;
    build(lson(x), l, mid);
    build(rson(x), mid + 1, r);
    pushup(x);
  }
  void update(int x, int pos) {
    if (t[x].l == t[x].r) {
      t[x].p = bp(wt[pos]);
      return ;
    } int mid = (t[x].l + t[x].r) >> 1;
    if (pos <= mid) update(lson(x), pos);
    else update(rson(x), pos);
    pushup(x);
  }
  node query(int x, int L, int R) {
    if (t[x].l >= L && t[x].r <= R)
      return t[x];
    int mid = (t[x].l + t[x].r) >> 1;
    if (L > mid) return query(rson(x), L, R);
    if (R <= mid) return query(lson(x), L, R);
    node ans, ls = query(lson(x), L, R), rs = query(rson(x), L, R);
    ans.l = ls.l; ans.r = rs.r;
    ans.p = mul(ls.p, rs.p);
    return ans;
  }
} t;
void update(int x, int val) {
  g[x][1] = g[x][1] - p[x] + val;
  p[x] = val; 
  while (x) {
    mat pre = t.query(1, dfn[top[x]], epos[top[x]]).p;
    t.update(1, dfn[x]);
    mat now = t.query(1, dfn[top[x]], epos[top[x]]).p;
    x = fa[top[x]];
    g[x][0] += max(now.a[0][0], now.a[1][0]) - max(pre.a[0][0], pre.a[1][0]);
    g[x][1] += now.a[0][0] - pre.a[0][0];
  }
}
signed main(void) {
  ios :: sync_with_stdio(false);
  cin.tie(0); cout.tie(0);
  cin >> n >> m;
  for (int i = 1; i <= n; ++i) cin >> p[i];
  for (int i = 1; i < n; ++i) {
    int u, v; cin >> u >> v;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  } dfs1(1); dfs2(1, 1);
  t.build(1, 1, n);
  for (int i = 1; i <= m; ++i) {
    int x, v; cin >> x >> v;
    update(x, v);
    mat p = t.query(1, 1, epos[1]).p;
    cout << max(p.a[0][0], p.a[1][0]) << '\n';
  }
  return 0;
}

标签:mat,int,max,笔记,son,bmatrix,ans,动态,DP
From: https://www.cnblogs.com/CTHOOH/p/17987973

相关文章

  • 哪款笔记软件支持电脑和手机互通数据?
    上班族在日常工作中,随手记录工作笔记已成为司空见惯的场景。例如:从快节奏的会议记录到灵感迸发的创意;跟踪项目进展,记录每个阶段的成果、问题和下一步计划;记录、更新工作任务清单等,工作笔记承载了职场人士丰富多彩的工作生活。为了能够随时随地记录工作事项,越来越多的职场人士提出......
  • 白话机器学习算法入门笔记
    机器学习中常见的十大算法包括:1.线性回归(LinearRegression)-用于预测连续值的简单线性回归模型。2.逻辑回归(LogisticRegression)-用于分类问题的logistic回归模型。3.决策树(DecisionTree)-一种流行的树形分类与回归方法。4.支持向量机(SVM)-一种二分类模型,Fi......
  • KMP 学习笔记
    前言——\(char\)与\(string\)有的时候\(char\)数组确实比\(string\)好用,且字符串长度很大时\(string\)会被卡掉,所以不要犯懒,老实用\(char\),\(string\)可以用但是慎用。同时很多情况下为了方便和减少出错,我们会想办法把字符串的坐标从\(0\simlen-1\)变成\(1......
  • 【学习笔记】 - 可持久化数据结构初步:可持久化线段树
    前置知识:权值线段树权值线段树每个节点不再是区间,而是值在某个范围内的个数可以用于求区间第\(k\)大值动态开点一个点只有在需要时才被创建正文什么是可持久化数据结构就是说这个数据结构能保留每一个历史版本且支持操作可持久化线段树又称函数式线段树/主席树......
  • WebAssembly入门笔记[4]:利用Global传递全局变量
    利用WebAssembly的导入导出功能可以灵活地实现宿主JavaScript程序与加载的单个wasm模块之间的交互,那么如何在宿主程序与多个wasm之间传递和共享数据呢?这就需要使用到Global这个重要的对象了。一、数值类型全局变量二、将JavaScript函数设置为全局变量三、利用全局变量处理字符......
  • 《梦断代码》阅读笔记2
    当今社会,软件已经成为人类生活中不可或缺的一部分,“人类文明运行于软件之上”的说法虽然有点自卖自夸,但它很是明确的反应了软件在人类社会中的地位。它存在于厨具里、汽车里、玩具里、建筑中,商业、科研、医疗、基础公共设施哪里都有它的影子,人类生存之所需都系于计算机代码这根易......
  • 《梦断代码》阅读笔记3
    寒假静下来读书的时间比较少,因此我并没有读完《梦断代码》这本有意思的书,以后会慢慢读的,现在说一说目前读完的部分的感受吧。首先,这本书深入讨论了软件开发的复杂性和编程的挑战性,尤其是在项目管理和时间规划方面。对于“软件时间”的分析让我意识到在实际编程中,时间管理并非总是......
  • GNN论文阅读笔记
    DOI10.1109/TNN.2008.2005605任何数据都可以由一张图(Graph)表示,图(Graph)是由一系列的点(vertex)与边(edge)的集合。机器学习的目标是:拟合一个函数τ(G,n) →Rm,即映射图G与其中某一节点n成一个m-dim的实数向量。根据实际任务,这种拟合有所偏向,大体可分为两类:关注于图特征的拟合......
  • 大三寒假学习进度笔记20
    今日对LangChain进行了一些了解。LangChain是一个强大的框架,旨在帮助开发人员使用语言模型构建端到端的应用程序。它提供了一套工具、组件和接口,可简化创建由大型语言模型(LLM)和聊天模型提供支持的应用程序的过程。LangChain可以轻松管理与语言模型的交互,将多个组件链接在一......
  • 【C语言进阶篇】动态内存常考笔试题
    (文章目录)......