题目描述

给定一棵n个点的树,点带点权。

有m次操作,每次操作给定x,y,表示修改点x的权值为y。

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

输入输出格式

输入格式:

第一行,n,m分别代表点数和操作数。

第二行,V1,V2,...,Vn,代表n个点的权值。

接下来n−1行,x,y,描述这棵树的n−1条边。

接下来m行,x,y,修改点x的权值为y。

输出格式:

对于每个操作输出一行一个整数,代表这次操作后的树上最大权独立集。

保证答案在int范围内

动态DP讲解:
 
考虑最大独立集转移方程:
$F[u][0]$ 表示 $u$ 点为根,不选$u$点的最大独立集.
$F[u][1]$ 表示 $u$ 点为根,选$u$点的最大独立集.
转移:
$F[u][0]=\sum_{v \in son[u]}max(F[u][0],F[u][1])$
$F[u][1]=\sum_{v \in son[u]}F[u][0]$
不难发现,每当我们修改一个点的点权,我们只会影响到该点到根节点这条链上的DP值.
如果树的高度是 $log(n)$,那么暴力跳链进行修改是完全可行的.
然而,题中树的高度可能会被恶意卡成 $O(n)$ 级别的.
那么,暴力跳链的总体复杂度就退化为 $O(nm)$ 了.
我们需要一个优秀的做法,使得每次修改都不受树高影响.
方法一 :  树链剖分 + 线段树 + 矩阵乘法.
注意的是,我们这里的矩阵乘法是长这样的:

Matrix operator*(Matrix a,Matrix b)
{
Matrix c;
c[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0]);
c[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1]);
c[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0]);
c[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1]);
return c;
}

我们用矩阵乘法维护每一次链上的转移.

我们用矩阵乘法维护每一次链上的转移.
根据常识,矩阵乘法要求满足结合律,而每一次状态转移也是满足结合律的.
对于每一个点开一个矩阵 $M_{u}$.
 $M_{u}=\begin{bmatrix} F_{u,0} & F_{u,0}\\ F_{u,1}  & -\infty \end{bmatrix}$
 考虑已经得到一个点 $i$ 的 $F_{i,0}$ 及 $F_{i,1}$,以及 $fa_{i}$ 的 $M_{fa_{i}}$.
 
 我们就可以通过矩阵乘法转移到 $fa_{i}$ 的方程了 !
 设 $u=fa_{i}$
 即 $\begin{bmatrix} F_{u,0} & F_{u,0}\\ F_{u,1}  & -\infty \end{bmatrix}\times\begin{bmatrix} F_{i,0}\\F_{i,1} \end{bmatrix}=\begin{bmatrix} F_{u,0}\\F_{u,1} \end{bmatrix}$
 是不是十分巧妙 ? (注:矩阵中的 $F[u][0]$ 与 $F[u][1]$ 指的是不包括该点重儿子的DP值,$i$ 是该点的重儿子,我们后文会提及)
 优秀的是,这些矩阵转移是由线段树来维护的.
 准确来说,重链上的线段树.
 我们引入树链剖分.
  
 
 
 
    
 
 
 
 考虑树剖的原理:
树剖将树剖成不超过 $log(n)$ 个重链,而树上任意两点最终都会蹦到同一个重链上.
 最坏也只会跳 $log(n)$ 次链顶,所以时间复杂度是十分优秀的. 
 而在动态 DP 中,我们也是用蹦链的方式来实现.
 即每条重链用一颗线段树维护,线段树来维护一段区间的矩阵乘法.
 这样,每次修改与查询就在对应的线段树中进行.
 
考虑修改:
在上文中,我们已经知道,每次修改只会影响到该点到根节点的一条路径,我们只需修改该点到根节点这条链上的信息即可.
现在在一条重链上的线段树进行单点修改,这是很简单的.
然后,我们再跳到该点所属重链的父亲上(设为 $fa$)
由于修改的是轻儿子,所以 $fa$ 的 $F[u][0]$ 与 $F[u][1]$ 都需要改变.
 
考虑查询:
由于我们在修改时已经将所有信息全部修改完毕,只需调用出根节点所在的重链的线段树, 并查一下区间矩阵乘法的结果即可.
 

Code:

#include<bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 300000
#define lson (now<<1)
#define rson ((now<<1)|1)
#define ll long long
const ll inf = 1e17;
using namespace std;
int hd[maxn],to[maxn],nex[maxn],V[maxn],hson[maxn],fa[maxn],dfn[maxn], ln[maxn],F[maxn][2],siz[maxn],top[maxn],bot[maxn];
int edges,tim,n,Q;
void add(int u,int v)
{
nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;
}
void dfs1(int u,int ff)
{
siz[u]=1,fa[u]=ff;
for(int i=hd[u];i;i=nex[i])
{
if(to[i]==ff) continue;
dfs1(to[i],u);
siz[u]+=siz[to[i]];
if(siz[to[i]] > siz[hson[u]]) hson[u]=to[i];
}
}
void dfs2(int u,int tp)
{
top[u]=tp,ln[++tim]=u,dfn[u]=tim;
if(hson[u])
dfs2(hson[u], tp), bot[u]=bot[hson[u]];
else
bot[u]=u;
for(int i=hd[u];i;i=nex[i])
{
if(to[i]==fa[u]||to[i]==hson[u]) continue;
dfs2(to[i],to[i]);
}
}
void dfs(int u)
{
F[u][0]=0,F[u][1]=V[u];
for(int i=hd[u];i;i=nex[i])
{
if(to[i]==fa[u]) continue;
dfs(to[i]);
F[u][0]+=max(F[to[i]][1],F[to[i]][0]);
F[u][1]+=F[to[i]][0];
}
}
struct Matrix
{
ll a[2][2];
ll*operator[](int x){ return a[x]; }
}t[maxn<<1],tmp[maxn<<1];
Matrix operator*(Matrix a,Matrix b)
{
Matrix c;
c[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0]);
c[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1]);
c[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0]);
c[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1]);
return c;
}
void Build(int now,int l,int r)
{
if(l==r)
{
int u=ln[l];
ll g0=0,g1=V[u];
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==fa[u]||v==hson[u]) continue;
g0+=max(F[v][0],F[v][1]);
g1+=F[v][0];
}
t[now]=tmp[l]=(Matrix) {g0,g0,g1,-inf};
return;
}
int mid=(l+r)>>1;
Build(lson,l,mid),Build(rson,mid+1,r);
t[now]=t[lson]*t[rson];
}
void Modify(int now,int l,int r,int p)
{
if(l==r)
{
t[now]=tmp[l];
return;
}
int mid=(l+r)>>1;
if(p<=mid) Modify(lson,l,mid,p);
else Modify(rson,mid+1,r,p);
t[now]=t[lson]*t[rson];
}
Matrix Query(int now,int l,int r,int L,int R)
{
if(L==l&&r==R) return t[now];
int mid=(l+r)>>1;
if(R<=mid) return Query(lson,l,mid,L,R);
if(L>mid) return Query(rson,mid+1,r,L,R);
return Query(lson,l,mid,L,mid)*Query(rson,mid+1,r,mid+1,R);
}
void Update(int u,int w)
{
tmp[dfn[u]][1][0]+=w-V[u],V[u]=w;
while(u)
{
Matrix a=Query(1,1,n,dfn[top[u]],dfn[bot[u]]);
Modify(1,1,n,dfn[u]);
Matrix b=Query(1,1,n,dfn[top[u]],dfn[bot[u]]);
u=fa[top[u]];
if(!u) break;
int x = dfn[u];
ll g0=a[0][0],g1=a[1][0],f0=b[0][0],f1=b[1][0];
tmp[x][0][0]=tmp[x][0][1]=tmp[x][0][0]+max(f0,f1)-max(g0,g1);
tmp[x][1][0]=tmp[x][1][0]+f0-g0;
}
}
int main()
{
// setIO("input");
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;++i) scanf("%d",&V[i]);
for(int i=1,u,v;i<n;++i)
{
scanf("%d%d",&u,&v),add(u,v),add(v,u);
}
dfs1(1,0),dfs2(1,1),dfs(1),Build(1,1,n);
while(Q--)
{
int x,w;
scanf("%d%d",&x,&w);
Update(x,w);
Matrix ans=Query(1,1,n,dfn[1],dfn[bot[1]]);
printf("%lld\n",max(ans[0][0],ans[1][0]));
}
return 0;
}

  

luoguP4719 【模板】动态 DP 线段树+树链剖分+矩阵乘法+动态DP的更多相关文章

  1. 51nod 1462 树据结构 | 树链剖分 矩阵乘法

    题目链接 51nod 1462 题目描述 给一颗以1为根的树. 每个点有两个权值:vi, ti,一开始全部是零. Q次操作: 读入o, u, d o = 1 对u到根上所有点的vi += d o = ...

  2. 形态形成场(矩阵乘法优化dp)

    形态形成场(矩阵乘法优化dp) 短信中将会涉及前\(k\)种大写字母,每个大写字母都有一个对应的替换式\(Si\),替换式中只会出现大写字母和数字,比如\(A→BB,B→CC0,C→123\),代表 ...

  3. 【bzoj1778】[Usaco2010 Hol]Dotp 驱逐猪猡 矩阵乘法+概率dp+高斯消元

    题目描述 奶牛们建立了一个随机化的臭气炸弹来驱逐猪猡.猪猡的文明包含1到N (2 <= N <= 300)一共N个猪城.这些城市由M (1 <= M <= 44,850)条由两 ...

  4. 斐波那契数列 矩阵乘法优化DP

    斐波那契数列 矩阵乘法优化DP 求\(f(n) \%1000000007​\),\(n\le 10^{18}​\) 矩阵乘法:\(i\times k\)的矩阵\(A\)乘\(k\times j\)的矩 ...

  5. 线段树&数链剖分

    傻逼线段树,傻逼数剖 线段树 定义: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 使用线段树可以快速的查找某一个节点在若干条线段中出现 ...

  6. 【清澄A1333】【整体二分+二维树状数组】矩阵乘法(梁盾)

    试题来源 2012中国国家集训队命题答辩 问题描述 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. 输入格式 第一行两个数N,Q,表示矩阵大小和询问组数: 接下来N行N列一共 ...

  7. 矩阵乘法优化DP复习

    前言 最近做毒瘤做多了--联赛难度的东西也该复习复习了. Warning:本文较长,难度分界线在"中场休息"部分,如果只想看普及难度的可以从第五部分直接到注意事项qwq 文中用(比 ...

  8. [BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】

    题目链接:BZOJ - 1009 题目分析 题目要求求出不包含给定字符串的长度为 n 的字符串的数量. 既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j ...

  9. 矩阵乘法优化DP

    本文讲一下一些基本的矩阵优化DP的方法技巧. 定义三个矩阵A,B,C,其中行和列分别为$m\times n,n \times p,m\times p$,(其中行是从上往下数的,列是从左往右数的) $C ...

随机推荐

  1. Servlet3.0中使用getPart进行文件上传

    这个先进些,简单些,但书上提供的例子不能使用,到处弄了弄才行. servlet代码: package cc.openhome; import java.io.InputStream; import j ...

  2. 洛谷—— P2424 约数和

    https://www.luogu.org/problem/show?pid=2424 题目背景 Smart最近沉迷于对约数的研究中. 题目描述 对于一个数X,函数f(X)表示X所有约数的和.例如:f ...

  3. linux环境下安装varnish

    Varnish是一款高性能的开源HTTP加速器,挪威最大的在线报纸 Verdens Gang 使用3台Varnish代替了原来的12台Squid,性能比以前更好. sudo apt-get insta ...

  4. HDU 4891 The Great Pan (字符串处理)

    题目链接:HDU 4891 The Great Pan 求一串字符有多少种不同的意思,当中关心'{','}'之间的'|'. 和'$','$'之间的空格,连续N个空格算N+1种. AC代码: #incl ...

  5. hdu1595find the longest of the shortest 最短路

    //给一个无向图,问删除一条边,使得从1到n的最短路最长 //问这个最长路 //这个删除的边必定在最短路上,假设不在.那么走这条最短路肯定比其它短 //枚举删除这条最短路的边,找其最长的即为答案 #i ...

  6. activeMQ公布订阅模式中中经常使用工具类

    package com.jms; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.j ...

  7. 修改input:file样式

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  8. C#中数据库备份还原 精简

    C#中数据库备份还原 使用前要导入SQLDMO.dll(在com组件中导入Microsoft SQLDMO Object Library即可) ///     /// DbOper类,主要应用SQLD ...

  9. B1821 [JSOI2010]Group 部落划分 Group 二分答案&&并查集

    这个题正解是最小生成树,但是...最大值最小?一看就是二分答案啊!不用多想,直接二分答案加暴力验证就行了. 题干: Description 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒 ...

  10. Help Me with the Game(模拟)

    http://poj.org/problem?id=2996 #include<stdio.h> #include<string.h> ][]; void find1(char ...