题目链接
题目解法
考虑朴素的
d
p
dp
dp,令
d
p
i
,
0
/
1
dp_{i,0/1}
dpi,0/1 表示以 1 为根的树,在
i
i
i 的子树中,
i
i
i 是否选的最大权独立集的权值大小
转移很
s
i
m
p
l
e
simple
simple,
合并 2 棵子树时可以
d
p
u
,
0
=
d
p
u
,
0
+
m
a
x
(
d
p
v
,
0
,
d
p
v
,
1
)
,
d
p
u
,
1
=
d
p
u
,
1
+
d
p
v
,
0
dp_{u,0}=dp_{u,0}+max(dp_{v,0},dp_{v,1}),\;dp_{u,1}=dp_{u,1}+dp_{v,0}
dpu,0=dpu,0+max(dpv,0,dpv,1),dpu,1=dpu,1+dpv,0
考虑每次修改时,只会更改当前点到根的路径上的
d
p
dp
dp 值,所以可以考虑快速维护这个
d
p
dp
dp 值的变化
于是根据以下 3 个性质,我们考虑使用重链剖分
- 每个点到根的路径上最多只会经过 l o g n log\;n logn 条轻边,所以考虑对于一段重链快速维护或者仅仅修改少量点
- 每条重链的末尾都是叶子结点,这有很好的性质(例如最后输出答案时只需要输出根所在重链的转移矩阵乘积即可,因为叶子结点的矩阵初值为 ∣ 0 0 ∣ \begin{vmatrix} 0\\0\end{vmatrix} 00 ,可以不用乘)
- 树剖之后重链的区间连续,可以用线段树维护一些区间信息,
例如矩乘
既然提到用矩乘维护转移,那么考虑对于每一个
u
u
u,维护转移矩阵表示不包括重儿子的转移矩阵
考虑到
∣
f
v
,
0
f
v
,
0
f
v
,
1
−
i
n
f
∣
∗
∣
f
u
,
0
f
u
,
1
∣
\begin{vmatrix} f_{v,0}\;\;f_{v,0}\\f_{v,1}\;-inf\end{vmatrix}*\begin{vmatrix}f_{u,0}\\f_{u,1}\end{vmatrix}
fv,0fv,0fv,1−inf
∗
fu,0fu,1
即为合并
u
,
v
u,v
u,v 之后的新矩阵
∣
f
u
,
0
′
f
u
,
1
′
∣
\begin{vmatrix}f_{u,0}'\\f_{u,1}'\end{vmatrix}
fu,0′fu,1′
,这里重定义矩阵乘法为
m
a
x
,
+
max,+
max,+,这仍然是满足结合律的(但不满足交换律)
所以最后的操作步骤就是再线段树上维护区间转移矩阵的乘积,修改时每次修改重链的头的父亲的转移矩阵,新的转移矩阵可以通过线段树上查询得到
询问之前说过了
时间复杂度
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)
#include <bits/stdc++.h>
using namespace std;
const int N(100100),inf(0x3f3f3f3f);
struct Matrix{
int n,m,a[3][3];
}trans[N],seg[N<<2];
Matrix operator *(const Matrix &A,const Matrix &B){
Matrix C;C.n=A.n,C.m=B.m;memset(C.a,0,sizeof(C.a));
for(int i=1;i<=C.n;i++) for(int j=1;j<=C.m;j++)
for(int k=1;k<=A.m;k++) C.a[i][j]=max(C.a[i][j],A.a[i][k]+B.a[k][j]);
return C;
}
int n,m,v[N],f[N][3];
int fa[N],siz[N],depth[N],son[N],top[N],dfn[N],ed[N],rv[N],dfs_clock;
int e[N<<1],h[N],ne[N<<1],idx;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void add(int x,int y){ e[idx]=y,ne[idx]=h[x],h[x]=idx++;}
void dfs1(int u){
depth[u]=depth[fa[u]]+1;
f[u][1]=0,f[u][2]=v[u],siz[u]=1;
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v!=fa[u]){
fa[v]=u,dfs1(v);
siz[u]+=siz[v];
f[u][1]+=max(f[v][1],f[v][2]),f[u][2]+=f[v][1];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
trans[u].n=2,trans[u].m=2,trans[u].a[1][1]=0,trans[u].a[2][1]=v[u],trans[u].a[2][2]=-inf;
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v!=son[u]&&v!=fa[u]) trans[u].a[1][1]+=max(f[v][2],f[v][1]),trans[u].a[2][1]+=f[v][1];
}
trans[u].a[1][2]=trans[u].a[1][1];
}
int dfs2(int u,int t){
top[u]=t,dfn[u]=++dfs_clock,rv[dfs_clock]=u;
if(!son[u]) return ed[u]=u;
ed[u]=dfs2(son[u],t);
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v!=son[u]&&v!=fa[u]) dfs2(v,v);
}
return ed[u];
}
void build(int l,int r,int x){
if(l==r){ seg[x]=trans[rv[l]];return;}
int mid=(l+r)>>1;
build(l,mid,x<<1),build(mid+1,r,x<<1^1);
seg[x]=seg[x<<1]*seg[x<<1^1];
}
void modify(int l,int r,int x,int pos){
if(l==r){ seg[x]=trans[rv[l]];return;}
int mid=(l+r)>>1;
if(mid>=pos) modify(l,mid,x<<1,pos);
else modify(mid+1,r,x<<1^1,pos);
seg[x]=seg[x<<1]*seg[x<<1^1];
}
Matrix query(int l,int r,int x,int L,int R){
if(L<=l&&r<=R) return seg[x];
int mid=(l+r)>>1;
if(mid>=L&&mid<R) return query(l,mid,x<<1,L,R)*query(mid+1,r,x<<1^1,L,R);
if(mid>=L) return query(l,mid,x<<1,L,R);
else return query(mid+1,r,x<<1^1,L,R);
}
void modify_point(int x,int y){
trans[x].a[2][1]+=y-v[x];v[x]=y;
while(true){
Matrix pre=query(1,n,1,dfn[top[x]],dfn[ed[x]]);
modify(1,n,1,dfn[x]);
Matrix aft=query(1,n,1,dfn[top[x]],dfn[ed[x]]);
x=fa[top[x]];
if(!x) break;
trans[x].a[1][1]-=max(pre.a[2][1],pre.a[1][1]),trans[x].a[2][1]-=pre.a[1][1];
trans[x].a[1][1]+=max(aft.a[2][1],aft.a[1][1]),trans[x].a[2][1]+=aft.a[1][1];
trans[x].a[1][2]=trans[x].a[1][1];
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) v[i]=read();
memset(h,-1,sizeof(h));
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y),add(y,x);
}
dfs1(1),dfs2(1,1);
build(1,n,1);
for(int i=1;i<=m;i++){
int x=read(),y=read();
modify_point(x,y);
Matrix ans=query(1,n,1,dfn[top[1]],dfn[ed[1]]);
printf("%d\n",max(ans.a[1][1],ans.a[2][1]));
}
return 0;
}