题目链接

题目大意:动态维护树上最大权独立集。

所谓动态 DP,就是把原先能用 DP 解决的问题变成动态维护 DP 值。如果 DP 数组可以支持合并两条链的话,可以直接用线段树维护,否则需要考虑把 DP 的转移改成矩阵,这样就可以用线段树维护矩阵,不过支持合并链以及维护矩阵都需要树链剖分,所以复杂度至少是两个 $\log^2n$。

对于这个题,显然 DP 数组并不满足链合并的性质,所以我们考虑如何采用矩阵转移,设 $f_{i,0/1}$ 表示以 $i$ 为根子树是否选 $i$ 的最大权值和,$g_{i,0/1}$ 表示以 $i$ 为根,仅考虑轻儿子是否选 $i$ 的最大权值和,设 $son_i$ 表示 $i$ 的重儿子。那么有:

$$
\begin{pmatrix}
g_{i,0}&g_{i,1}\
g_{i,1}&-\infty
\end{pmatrix}
\begin{pmatrix}
f_{son_i,0}\
f_{son_i,1}
\end{pmatrix}

\begin{pmatrix}
f_{i,0}\
f_{i,1}
\end{pmatrix}
$$

注意这里的矩阵乘法是在 $(+,\max)$ 下定义的。

众所周知,矩阵乘法只需要满足 $(\times,+)$ 满足结合率,交换律,而且 $\times$ 对 $+$ 有分配率。

这样,对于一次修改,设修改的点为 $u$,那么只需要修改 $g_{u,1}$ 的值,以及从 $u$ 往上到根节点所有经过的轻边父亲节点的 $g$,这些 $g$ 只需要求出重链顶端的 $f$ 值,暴力修改即可。

这样我们需要维护树上矩阵乘法,单点修改。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include<bits/stdc++.h>
#define mset(a,b) memset((a),(b),sizeof((a)))
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define dec(i,l,r) for(int i=(r);i>=(l);i--)
#define cmax(a,b) (((a)<(b))?(a=b):(a))
#define cmin(a,b) (((a)>(b))?(a=b):(a))
#define Next(k) for(int x=head[k];x;x=li[x].next)
#define vc vector
#define ar array
#define pi pair
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define N 100010
#define M number
using namespace std;

typedef double dd;
typedef long double ld;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
//#define int long long
typedef pair<int,int> P;
typedef vector<int> vi;

const int INF=0x3f3f3f3f;
const dd eps=1e-9;

template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}

struct Matrix{
int n,m,a[2][2];
inline void Clear(){
mset(a,0);n=m=0;
}
inline void Print(){
printf("%d %d\n",n,m);
rep(i,0,n-1){
rep(j,0,m-1) printf("%d ",a[i][j]);puts("");
}
}
inline Matrix operator * (const Matrix &b)const{
// printf("n=%d m=%d b.n=%d b.m=%d\n",n,m,b.n,b.m);
Matrix c;c.Clear();assert(m==b.n);c.n=n;c.m=b.m;
rep(i,0,c.n-1)rep(j,0,c.m-1){
c.a[i][j]=-INF;
rep(k,0,m-1){
cmax(c.a[i][j],a[i][k]+b.a[k][j]);
}
}
return c;
}
inline void GetI(int len){
(*this).Clear();n=m=len;
rep(i,0,n-1)rep(j,0,m-1) a[i][j]=-INF;
rep(i,0,len-1) a[i][i]=0;
}
};
int en[N],top[N],siz[N],dep[N],id[N],rk[N],tot,fa[N],son[N],n,m,g[N][2],f[N][2],a[N];
vi e[N];
Matrix ze;
struct SegmentTree{
#define ls(k) k<<1
#define rs(k) k<<1|1
Matrix a[N<<2];
inline void PushUp(int k){
a[k]=a[ls(k)]*a[rs(k)];
}
inline void Change(int k,int l,int r,int w,int i,int j,int x){
if(l==r){
a[k].a[i][j]+=x;return;
}int mid=(l+r)>>1;
if(w<=mid) Change(ls(k),l,mid,w,i,j,x);
else Change(rs(k),mid+1,r,w,i,j,x);PushUp(k);
}
inline Matrix Ask(int k,int l,int r,int z,int y){
if(z<=l&&r<=y){
return a[k];
}int mid=(l+r)>>1;Matrix now;now.GetI(2);
if(z<=mid) now=now*Ask(ls(k),l,mid,z,y);
if(mid<y) now=now*Ask(rs(k),mid+1,r,z,y);
return now;
}
inline void Build(int k,int l,int r){
if(l==r){
int i=rk[l];
a[k].a[0][0]=g[i][0];a[k].a[0][1]=g[i][0];a[k].a[1][0]=g[i][1];a[k].a[1][1]=-INF;a[k].m=a[k].n=2;
return;
}int mid=(l+r)>>1;Build(ls(k),l,mid);Build(rs(k),mid+1,r);PushUp(k);
}
inline void dfs(int k,int l,int r){
int mid=(l+r)>>1;
printf("k=%d\n",k);a[k].Print();if(l==r) return;
dfs(ls(k),l,mid);dfs(rs(k),mid+1,r);
}
}st;

inline void dfs(int k,int fat){
fa[k]=fat;siz[k]=1;dep[k]=dep[fat]+1;for(int to:e[k])if(to!=fat){dfs(to,k);siz[k]+=siz[to];if(siz[son[k]]<siz[to]) son[k]=to;}
}
inline int Dfs(int k,int t){
top[k]=t;id[k]=++tot;rk[tot]=k;int fia=-1;if(son[k])fia=Dfs(son[k],t);for(int to:e[k])if(to!=son[k]&&to!=fa[k]){Dfs(to,to);}if(fia==-1) fia=k;
en[k]=fia;return en[k];
}
inline void DFs(int k){
f[k][1]=a[k];f[k][0]=0;g[k][1]=a[k];g[k][0]=0;
for(int to:e[k])if(to!=fa[k]) DFs(to);
for(int to:e[k])if(to!=fa[k]){
f[k][1]+=f[to][0];f[k][0]+=max(f[to][0],f[to][1]);
if(to!=son[k]) g[k][1]+=f[to][0],g[k][0]+=max(f[to][0],f[to][1]);
}
}
inline void Change(int u,int v){
Matrix ch;ch.Clear();
ch.a[1][0]+=-a[u]+v;a[u]=v;
while(u){
Matrix last=st.Ask(1,1,n,id[top[u]],id[en[u]])*ze;
rep(i,0,1)rep(j,0,1)if(ch.a[i][j])st.Change(1,1,n,id[u],i,j,ch.a[i][j]);
Matrix now=st.Ask(1,1,n,id[top[u]],id[en[u]])*ze;
u=fa[top[u]];ch.Clear();
ch.a[0][0]=-max(last.a[0][0],last.a[1][0])+max(now.a[0][0],now.a[1][0]);
ch.a[0][1]=ch.a[0][0];
ch.a[1][0]=-last.a[0][0]+now.a[0][0];
}
}

int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);read(m);rep(i,1,n) read(a[i]);
rep(i,1,n-1){
int u,v;read(u);read(v);e[u].pb(v);e[v].pb(u);
}
ze.Clear();ze.n=2;ze.m=1;
dfs(1,0);Dfs(1,1);DFs(1);st.Build(1,1,n);
rep(i,1,m){
int u,v;read(u);read(v);
Change(u,v);
Matrix now=st.Ask(1,1,n,id[1],id[en[1]])*ze;
printf("%d\n",max(now.a[0][0],now.a[1][0]));
}
return 0;
}

/*
+ 忘记 Build,Build 函数写错
+ 矩阵乘法写错
+ += 写成 =
+ 单位矩阵初始化错误
+ 1 写成 i
+ 忘记 a[u]=v
*/