背景:
N
O
I
P
NOIP
NOIP的坑。
现在开始学…
题目传送门:
https://www.luogu.org/problemnew/show/P4719
题意:
求动态的树上最大独立集(就是选出若干个点,使得这些点中两两没有连边,并且权值和最大)。
思路:
考虑常规做法。
f
[
i
]
[
0
]
f[i][0]
f[i][0]表示以
i
i
i点为根的子树,不选
i
i
i点的最大独立集的和;
f
[
i
]
[
1
]
f[i][1]
f[i][1]表示以
i
i
i点为根的子树,选
i
i
i点的最大独立集的和。
显然有转移方程:
f
[
i
]
[
0
]
=
∑
u
∈
i
.
s
o
n
m
a
x
(
f
[
u
]
[
0
]
,
f
[
u
]
[
1
]
)
f[i][0]=\sum_{u∈i.son}max(f[u][0],f[u][1])
f[i][0]=∑u∈i.sonmax(f[u][0],f[u][1])
f
[
i
]
[
1
]
=
v
a
l
u
e
[
i
]
+
∑
u
∈
i
.
s
o
n
f
[
u
]
[
0
]
f[i][1]=value[i]+\sum_{u∈i.son}f[u][0]
f[i][1]=value[i]+∑u∈i.sonf[u][0]。
至于修改操作,有一个小优化:选取点
x
x
x到根的路径上的
d
p
dp
dp数组
f
f
f修改即可。
总时间复杂度:
Θ
(
n
∗
q
)
\Theta(n*q)
Θ(n∗q)。
有这个小优化我们可新得到一个方程:
f
[
i
]
[
0
]
f[i][0]
f[i][0]表示从根到点
i
i
i的路径中,不选
i
i
i点的最大独立集的和;
f
[
i
]
[
1
]
f[i][1]
f[i][1]表示从根到点
i
i
i的路径中,选
i
i
i点的最大独立集的和。
f
[
i
]
[
0
]
=
m
a
x
(
f
[
i
−
1
]
[
0
]
,
f
[
i
−
1
]
[
1
]
)
f[i][0]=max(f[i-1][0],f[i-1][1])
f[i][0]=max(f[i−1][0],f[i−1][1])
f
[
i
]
[
1
]
=
v
a
l
u
e
[
i
]
+
f
[
i
−
1
]
[
0
]
f[i][1]=value[i]+f[i-1][0]
f[i][1]=value[i]+f[i−1][0]。
我们可以写成矩阵乘法的形式:
[
0
0
v
a
l
u
e
[
i
]
−
∞
]
∗
[
f
[
i
−
1
]
[
0
]
f
[
i
−
1
]
[
1
]
]
=
[
f
[
i
]
[
0
]
f
[
i
]
[
1
]
]
\left[ \begin{matrix} 0 & 0\\ value[i] & -∞ \end{matrix} \right]* \left[ \begin{matrix} f[i-1][0]\\ f[i-1][1]\\ \end{matrix} \right]= \left[ \begin{matrix} f[i][0]\\ f[i][1]\\ \end{matrix} \right]
[0value[i]0−∞]∗[f[i−1][0]f[i−1][1]]=[f[i][0]f[i][1]]
手模一下即可。
但现在只是在一个路径上,在一棵树上该怎么办呢?
考虑
L
C
T
LCT
LCT。
[
i
]
[
0
]
,
[
i
]
[
1
]
[i][0],[i][1]
[i][0],[i][1]定义如上。
用
f
[
i
]
[
]
f[i][]
f[i][]表示所有儿子的
d
p
dp
dp数组,
g
[
i
]
[
]
g[i][]
g[i][]表示轻儿子的
d
p
dp
dp数组。
那么就有:
g
[
i
]
[
0
]
=
∑
u
∈
i
.
l
i
g
h
t
_
s
o
n
m
a
x
(
g
[
u
]
[
0
]
,
g
[
u
]
[
1
]
)
g[i][0]=\sum_{u∈i.light\_son}max(g[u][0],g[u][1])
g[i][0]=∑u∈i.light_sonmax(g[u][0],g[u][1])
g
[
i
]
[
1
]
=
v
a
l
u
e
[
i
]
+
∑
u
∈
i
.
l
i
g
h
t
_
s
o
n
g
[
u
]
[
0
]
g[i][1]=value[i]+\sum_{u∈i.light\_son}g[u][0]
g[i][1]=value[i]+∑u∈i.light_song[u][0]。
f
[
i
]
[
0
]
=
g
[
i
]
[
0
]
+
m
a
x
(
f
[
i
.
h
e
a
v
y
_
s
o
n
]
[
0
]
,
f
[
i
.
h
e
a
v
y
_
s
o
n
]
[
1
]
)
f[i][0]=g[i][0]+max(f[i.heavy\_son][0],f[i.heavy\_son][1])
f[i][0]=g[i][0]+max(f[i.heavy_son][0],f[i.heavy_son][1])
f
[
i
]
[
1
]
=
g
[
i
]
[
1
]
+
f
[
i
.
h
e
a
v
y
_
s
o
n
]
[
0
]
f[i][1]=g[i][1]+f[i.heavy\_son][0]
f[i][1]=g[i][1]+f[i.heavy_son][0]。
注意最后一条式子最后一项是
[
0
]
[0]
[0],因为在一条链上不存在同时选儿子和父亲(
L
C
T
LCT
LCT的性质)。
同理也可以写成矩阵乘法的形式:
[
0
0
v
a
l
u
e
[
i
]
−
∞
]
∗
[
g
[
i
.
l
i
g
h
t
_
s
o
n
]
[
0
]
g
[
i
.
l
i
g
h
t
_
s
o
n
]
[
1
]
]
=
[
g
[
i
]
[
0
]
g
[
i
]
[
1
]
]
\left[ \begin{matrix} 0 & 0\\ value[i] & -∞ \end{matrix} \right]* \left[ \begin{matrix} g[i.light\_son][0]\\ g[i.light\_son][1]\\ \end{matrix} \right]= \left[ \begin{matrix} g[i][0]\\ g[i][1]\\ \end{matrix} \right]
[0value[i]0−∞]∗[g[i.light_son][0]g[i.light_son][1]]=[g[i][0]g[i][1]]
[
g
[
i
.
l
i
g
h
t
_
s
o
n
]
[
0
]
g
[
i
.
l
i
g
h
t
_
s
o
n
]
[
0
]
g
[
i
.
l
i
g
h
t
_
s
o
n
]
[
1
]
−
∞
]
∗
[
f
[
i
.
h
e
a
v
y
_
s
o
n
]
[
0
]
f
[
i
.
h
e
a
v
y
_
s
o
n
]
[
1
]
]
=
[
f
[
i
]
[
0
]
f
[
i
]
[
1
]
]
\left[ \begin{matrix} g[i.light\_son][0] & g[i.light\_son][0]\\ g[i.light\_son][1] & -∞ \end{matrix} \right]* \left[ \begin{matrix} f[i.heavy\_son][0]\\ f[i.heavy\_son][1]\\ \end{matrix} \right]= \left[ \begin{matrix} f[i][0]\\ f[i][1]\\ \end{matrix} \right]
[g[i.light_son][0]g[i.light_son][1]g[i.light_son][0]−∞]∗[f[i.heavy_son][0]f[i.heavy_son][1]]=[f[i][0]f[i][1]]
在
a
c
c
e
s
s
access
access时维护
g
g
g数组(有关轻重儿子的改变),在
p
u
s
h
u
p
pushup
pushup时维护
f
f
f数组即可。
我们可以发现
g
g
g数组是不需要存下来的,因为我们的根本目的是得到
f
f
f数组,不妨在预处理时就搞定
f
,
g
f,g
f,g数组,即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF (int)1e9
using namespace std;
struct CALC
{
int a[2][2];
void NEW(const int A,const int B) {a[0][0]=a[0][1]=A,a[1][0]=B,a[1][1]=-INF;}
int MAX() {return max(a[0][0],a[1][0]);}
friend CALC operator * (const CALC A,const CALC B)
{
CALC C;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
C.a[i][j]=max(A.a[i][0]+B.a[0][j],A.a[i][1]+B.a[1][j]);
return C;
}
};
struct node1
{
CALC d;
int fa,lazy,son[2],f[2];
node1() {fa=lazy=son[0]=son[1]=0;}
} tr[300010];
struct node2{int x,y,next;} a[300010];
int value[300010],last[300010];
int n,m,len=0;
bool isroot(int x)
{
return tr[tr[x].fa].son[0]!=x&&tr[tr[x].fa].son[1]!=x;
}
bool isson(int x)
{
return x==tr[tr[x].fa].son[1];
}
void change(int x)
{
if(!x) return;
swap(tr[x].son[0],tr[x].son[1]);
tr[x].lazy^=1;
}
void pushup(int x)
{
tr[x].d.NEW(tr[x].f[0],tr[x].f[1]);
if(tr[x].son[0]) tr[x].d=tr[tr[x].son[0]].d*tr[x].d;
if(tr[x].son[1]) tr[x].d=tr[x].d*tr[tr[x].son[1]].d;
}
void pushdown(int x)
{
if(!tr[x].lazy) return;
change(tr[x].son[0]),change(tr[x].son[1]);
tr[x].lazy=0;
}
void rot(int x)
{
int w=isson(x),y=tr[x].fa,yy=tr[y].fa;
tr[y].son[w]=tr[x].son[w^1];
if(tr[y].son[w]) tr[tr[y].son[w]].fa=y;
tr[x].fa=yy;
if(!isroot(y)) tr[yy].son[isson(y)]=x;
tr[x].son[w^1]=y;tr[y].fa=x;
pushup(y);
}
int sta[300010];
int top;
void splay(int x)
{
sta[top=1]=x;
for(int i=x;!isroot(i);i=tr[i].fa)
sta[++top]=tr[i].fa;
while(top) pushdown(sta[top--]);
for(int y=tr[x].fa;!isroot(x);rot(x),y=tr[x].fa)
if(!isroot(y)) isson(x)^isson(y)?rot(x):rot(y);
pushup(x);
}
void access(int x)
{
for(int y=0;x;x=tr[y=x].fa)
{
splay(x);
if(tr[x].son[1])
{
tr[x].f[0]+=tr[tr[x].son[1]].d.MAX();
tr[x].f[1]+=tr[tr[x].son[1]].d.a[0][0];
}
if(y)
{
tr[x].f[0]-=tr[y].d.MAX();
tr[x].f[1]-=tr[y].d.a[0][0];
}
tr[x].son[1]=y;
pushup(x);
}
}
void dfs(int x,int fa)
{
tr[x].fa=fa;
tr[x].f[1]=value[x];
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa) continue;
dfs(y,x);
tr[x].f[0]+=max(tr[y].f[0],tr[y].f[1]);
tr[x].f[1]+=tr[y].f[0];
}
tr[x].d.NEW(tr[x].f[0],tr[x].f[1]);
}
void ins(int x,int y)
{
a[++len]=(node2){x,y,last[x]}; last[x]=len;
}
int main()
{
int x,y;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&value[i]);
for(int i=1;i<n;i++)
{
scanf("%d %d",&x,&y);
ins(x,y),ins(y,x);
}
dfs(1,0);
for(int i=1;i<=m;i++)
{
scanf("%d %d",&x,&y);
access(x),splay(x);
tr[x].f[1]+=y-value[x],value[x]=y;
pushup(x);
splay(1);
printf("%d\n",tr[1].d.MAX());
}
}