题解

在冬令营上听到冬眠的东西,现在都是板子了猫锟真的是好毒瘤啊(雾)

(立个flag,我去thusc之前要把WC2018T1乱搞过去= =)

好的,我们可以参考猫锟的动态动态dp的课件,然后你发现你什么都看不懂(菜啊

但是我们仔细看一看,可以发现用数据结构维护矩阵,那么我们尝试构造一个矩阵

$\begin{bmatrix}

\ g_{u,0} & g_{u,0}\

g_{u,1} & 0

\end{bmatrix}

\begin{bmatrix}

f_{son[u],0}\

f_{son[u],1}

\end{bmatrix}

\begin{bmatrix}

f_{u,0}\

f_{u,1}

\end{bmatrix}\(
其中\)g_{u,0}\(表示不选u,对于u的子树,除了u的重儿子所在子树,u的轻儿子最大独立集是多少
\)g_{u,1}\(表示选了u
\)f_{u,0}\(表示不选u,以u为根的子树最大独立集是多少
\)f_{u,1}$表示选了u,以u为根的子树最大独立集是多少

这个矩阵的运算不是加法和乘法,而是加法和取max

也就是\(c_{i,j} = max(a_{i,k} + b_{k,j})\)

也是满足结合律的

这样我们就可以用一个树链剖分来维护这些矩阵了

注意一下,合并矩阵的时候是左右合并,但是乘法的时候一定先从右边乘

举个例子,我们的运算实际是这样的

一条链:

3 - 4 - 5

3是4的父亲,4是5的父亲

\(\begin{bmatrix}
\ g_{3,0} & g_{3,0}\\
g_{3,1} & 0
\end{bmatrix}
\begin{bmatrix}
\ g_{4,0} & g_{4,0}\\
g_{4,1} & 0
\end{bmatrix}
\begin{bmatrix}
f_{5,0}\\
f_{5,1}
\end{bmatrix} =
\begin{bmatrix}
f_{3,0}\\
f_{3,1}
\end{bmatrix}\)

也就是5先和4的矩阵结合,再和3的矩阵结合

实际上写代码的时候,既然每一条链的最后一定是一个叶子节点,我们就可以直接把f0设成0,f1设成0去做矩阵乘法,乘上这条链上所有的矩阵作为链顶的f值

代码

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
//#define ivorysi
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define mo 974711
#define RG register
#define MAXN 100005
#define debug
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) putchar('-'),x = -x;
if(x >= 10) out(x / 10);
putchar('0' + x % 10);
}
const int MOD = 1000000007;
int mul(int a,int b) {
return 1LL * a * b % MOD;
}
int inc(int a,int b) {
a = a + b;
if(a >= MOD) a -= MOD;
return a;
} int fpow(int x,int c) {
int res = 1,t = x % MOD;
while(c) {
if(c & 1) res = mul(res,t);
t = mul(t,t);
c >>= 1;
}
return res;
}
int N,Q;
int64 val[MAXN],g[MAXN][2],f[MAXN][2];
int top[MAXN],btm[MAXN],dfn[MAXN],fa[MAXN],dep[MAXN],son[MAXN],idx,siz[MAXN],L[MAXN],pos[MAXN];
int cnt = 0;
struct node {
int to,next;
}E[MAXN * 2];
int head[MAXN],sumE;
struct tr_node {
int l,r;
int64 M[2][2];
}tr[MAXN * 4];
void add(int u,int v) {
E[++sumE].to = v;
E[sumE].next = head[u];
head[u] = sumE;
}
void dfs1(int u) {
dep[u] = dep[fa[u]] + 1;
siz[u] = 1;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(v != fa[u]) {
fa[v] = u;
dfs1(v);
siz[u] += siz[v];
if(siz[v] > siz[son[u]]) son[u] = v;
}
}
}
void dfs2(int u) {
dfn[u] = ++idx;
L[idx] = u;
if(!top[u]) {top[u] = u;}
if(son[u]) {
top[son[u]] = top[u];
dfs2(son[u]);
btm[u] = btm[son[u]];
}
g[u][1] = val[u];g[u][0] = 0;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(v != son[u] && v != fa[u]) {
dfs2(v);
g[u][0] += max(f[v][0],f[v][1]);
g[u][1] += max(0LL,f[v][0]);
}
}
f[u][1] = g[u][1] + max(f[son[u]][0],0LL);
f[u][0] = g[u][0] + max(f[son[u]][1],f[son[u]][0]);
if(!son[u]) btm[u] = u;
}
void update(int u) {
int L = u << 1,R = u << 1 | 1;
tr[u].M[0][0] = max(tr[L].M[0][0] + tr[R].M[0][0],tr[L].M[0][1] + tr[R].M[1][0]);
tr[u].M[0][1] = max(tr[L].M[0][0] + tr[R].M[0][1],tr[L].M[0][1] + tr[R].M[1][1]);
tr[u].M[1][0] = max(tr[L].M[1][0] + tr[R].M[0][0],tr[L].M[1][1] + tr[R].M[1][0]);
tr[u].M[1][1] = max(tr[L].M[1][0] + tr[R].M[0][1],tr[L].M[1][1] + tr[R].M[1][1]);
}
void build(int u,int l,int r) {
tr[u].l = l;tr[u].r = r;
if(l == r) {
int v = L[l];
tr[u].M[0][0] = g[v][0];tr[u].M[0][1] = g[v][0];
tr[u].M[1][0] = g[v][1];tr[u].M[1][1] = 0;
pos[v] = u;
return ;
}
int mid = (l + r) >> 1;
build(u << 1,l,mid);build(u << 1 | 1,mid + 1,r);
update(u);
}
void Query(int u,int L,int R,int64 &f0,int64 &f1) {
if(tr[u].l == L && tr[u].r == R) {
int64 x = max(f0 + tr[u].M[0][0],f1 + tr[u].M[0][1]);
int64 y = max(f0 + tr[u].M[1][0],f1 + tr[u].M[1][1]);
f0 = x;f1 = y;
return;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if(R <= mid) Query(u << 1,L,R,f0,f1);
else if(L > mid) Query(u << 1 | 1,L,R,f0,f1);
else {
Query(u << 1 | 1,mid + 1,R,f0,f1);
Query(u << 1,L,mid,f0,f1);
}
}
void Change(int x) {
int t = pos[x] >> 1;
while(t) {
update(t);
t >>= 1;
}
}
void Change_tr(int x) {
while(1) {
int a = top[x];
if(fa[a] != 0) {
g[fa[a]][0] -= max(f[a][0],f[a][1]);
g[fa[a]][1] -= max(f[a][0],0LL);
}
f[a][0] = 0;f[a][1] = 0;
Query(1,dfn[top[x]],dfn[btm[x]],f[a][0],f[a][1]);
if(fa[a] == 0) break;
g[fa[a]][0] += max(f[a][0],f[a][1]);
g[fa[a]][1] += max(f[a][0],0LL);
x = fa[a];
tr[pos[x]].M[0][0] = tr[pos[x]].M[0][1] = g[x][0];
tr[pos[x]].M[1][0] = g[x][1];tr[pos[x]].M[1][1] = 0;
Change(x);
}
}
void Init() {
read(N);read(Q);
for(int i = 1 ; i <= N ; ++i) read(val[i]);
int u,v;
for(int i = 1 ; i < N ; ++i) {
read(u);read(v);
add(u,v);add(v,u);
}
dfs1(1);
dfs2(1);
build(1,1,N);
}
void Solve() {
int x;int64 y; while(Q--) {
++cnt;
read(x);read(y);
g[x][1] = g[x][1] - val[x] + y;
tr[pos[x]].M[1][0] = g[x][1];
val[x] = y;
Change(x);
Change_tr(x);
out(max(f[1][0],f[1][1]));enter; }
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
Solve();
return 0;
}

【洛谷】P4643 【模板】动态dp的更多相关文章

  1. 【洛谷P4719】动态dp 动态dp模板

    题目大意:给你一颗$n$个点的树,点有点权,有$m$次操作,每次操作给定$x$,$y$,表示修改点$x$的权值为$y$. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 数据范围:$n,m≤ ...

  2. 【洛谷4719】 动态dp(树链剖分,dp,矩阵乘法)

    前言 其实我只是为了过掉模板而写的ddp,实际应用被吊着锤 Solution 并不想写详细的过程 一句话过程:将子树中轻儿子的贡献挂到这个点上面来 详细版:(引用yyb) 总结一下的话,大致的过程是这 ...

  3. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  4. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  5. 洛谷P3928 Sequence2(dp,线段树)

    题目链接: 洛谷 题目大意在描述底下有.此处不赘述. 明显是个类似于LIS的dp. 令 $dp[i][j]$ 表示: $j=1$ 时表示已经处理了 $i$ 个数,上一个选的数来自序列 $A[0]$ 的 ...

  6. 洛谷P2224 [HNOI2001] 产品加工 [DP补完计划,背包]

    题目传送门 产品加工 题目描述 某加工厂有A.B两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成.由于受到机器性能和产品特性的限制,不同的机器加工同一产品所需的时间会不同,若同时 ...

  7. 洛谷P1108 低价购买[DP | LIS方案数]

    题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...

  8. NOIP2017提高组Day2T2 宝藏 洛谷P3959 状压dp

    原文链接https://www.cnblogs.com/zhouzhendong/p/9261079.html 题目传送门 - 洛谷P3959 题目传送门 - Vijos P2032 题意 给定一个 ...

  9. 洛谷P3375 [模板]KMP字符串匹配

    To 洛谷.3375 KMP字符串匹配 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果 ...

  10. 洛谷P1244 青蛙过河 DP/思路

    又是一道奇奇怪怪的DP(其实是思路题). 原文戳>>https://www.luogu.org/problem/show?pid=1244<< 这题的意思给的挺模糊,需要一定的 ...

随机推荐

  1. 转:UIView之userInteractionEnabled属性介绍

    属性作用 该属性值为布尔类型,如属性本身的名称所释,该属性决定UIView是否接受并响应用户的交互. 当值设置为NO后,UIView会忽略那些原本应该发生在其自身的诸如touch和keyboard等用 ...

  2. nginx配置详情(总结)

    Nginx简介 Nginx是一款开源代码的高性能HTTP服务器和反向代理服务器,同时支持IMAP/POP3/SMTP代理服务 Nginx工作原理 Nginx由内核和模块组成,完成工作是通过查找配置文件 ...

  3. 贪心问题 POJ 2393 Yogurt factory

    题目:http://poj.org/problem?id=2393 题意:N周,每周生成牛奶(任意!),每周成本为c_i(1~5000),每周出货 y_i:出货可以使用该周生产的,也可以用之前的储存的 ...

  4. stl第二级空间配置器详解(1)

    SGI STL考虑到小型内存区块的碎片问题,设计了双层级配置器,第一级配置直接使用malloc()和free():第二级配置器则视情况采用不同的策略,当配置区大于128bytes时,直接调用第一级配置 ...

  5. Java并发编程原理与实战三十七:线程池的原理与使用

    一.简介 线程池在我们的高并发环境下,实际应用是非常多的!!适用频率非常高! 有过使用过Executors框架的朋友,可能不太知道底层的实现,这里就是讲Executors是由ThreadPoolExe ...

  6. Java并发编程原理与实战二十七:循环栅栏:CyclicBarrier

    昨天我们学习了倒计数功能的等待,今天我们学习的是循环栅栏:CyclicBarrier.下面我们就开始吧: 1.CyclicBarrier简介CyclicBarrier,是JDK1.5的java.uti ...

  7. Could not parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location

    spring-boot项目,生产环境运行一段时间后,上传图片报错,如下: threw exception [Request processing failed; nested exception is ...

  8. ArchLinux升级后deadbeef无法正常启动的解决办法

    deadbeef是一款简约而不简单的音乐播放器, 占资源少, 支持的格式却不少. 昨天对ArchLinux进行了一次全面升级, 经历种种惊险, 终于跨越了从 glibc-2.16 到 glibc-2. ...

  9. HDU 2571 命运 (入门dp)

    题目链接 题意:二维矩阵,左上角为起点,右下角为终点,如果当前格子是(x,y),下一步可以是(x+1,y),(x,y+1)或者(x,y*k) ,其中k>1.问最大路径和. 题解:入门dp,注意负 ...

  10. HDU 1176 排列2 全排列

    解题报告:给出四个数,然后要你把这四个数组合成的各不相同的四位数按照从小到大的顺序输出来,然后如果最高位是0的话不能输出来,还有最高位是数字如果一样的话,则放在同一行输出. 本来是个比较简单的生成全排 ...