题目
题目链接:https://www.luogu.com.cn/problem/P4719
给定一棵\(n\)个点的树,点带点权。
有\(m\)次操作,每次操作给定\(x,y\),表示修改点\(x\)的权值为\(y\)。
你需要在每次操作之后求出这棵树的最大权独立集的权值大小。
思路
调到心态爆炸。。。从前天晚上开始就刚这道题。。。
最大权独立子集即“选出若干个不相邻的点使得他们的权值最大”。——摘自AKIOI的神仙
若没有修改,这道题就是树形\(dp\)的入门题,设\(f[x][0/1]\)表示以\(x\)为根的子树,节点\(i\)选不选的最大权独立子集。那么有转移
\[f[u][0]=\sum_{v\in u's\ son}max(f[v][0],f[v][1])
\]
\[f[u][1]=\sum_{v\in u's\ son}f[v][0]
\]
而\(ddp\)是维护序列问题的,我们考虑将这棵树剖一下,变成若干条重链计算。
那么既然我们重链用矩阵乘法维护,那么轻链就要先计算出来。设\(g[x][0/1]\)表示不看\(x\)的重儿子的最大权独立子集,转移显然。
这样我们就可以改写\(f\)的转移
\[f[u][0]=g[u][0]+max(f[v][0],f[v][1])
\]
\[f[u][1]=g[u][1]+f[v][0]
\]
其中\(v\)是\(u\)的重儿子。
写成矩阵,有
\[\begin{bmatrix}f[v][0]
\\ f[v][1]
\end{bmatrix}
\begin{bmatrix}
g[u][0] &g[u][0] \\
g[u][1] &-\infty
\end{bmatrix}
=
\begin{bmatrix}
f[u][0]\\
f[u][1]
\end{bmatrix}\]
假设我们修改点\(x\),这样我们就可以修改\(g[top[x]]\),然后再利用\(g[top[x]]\)修改\(g[fa[top[x]]]\),再利用重链往上修改。
细节算是比较多吧,调的很恶心。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=100010,Inf=107374323;
int head[N],a[N],f[N][2],g[N][2],son[N],rk[N],id[N],top[N],size[N],fa[N],end[N];
int n,Q,tot;
struct edge
{
int next,to;
}e[N*2];
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
void dfs1(int x,int father)
{
f[x][0]=0; f[x][1]=a[x];
size[x]=1; fa[x]=father;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=father)
{
dfs1(v,x);
size[x]+=size[v];
f[x][0]+=max(f[v][0],f[v][1]);
f[x][1]+=f[v][0];
if (size[v]>size[son[x]]) son[x]=v;
}
}
}
void dfs2(int x,int tp)
{
g[x][0]=0; g[x][1]=a[x];
id[x]=++tot; rk[tot]=x; top[x]=tp; end[tp]=tot;
if (son[x]) dfs2(son[x],tp);
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa[x] && v!=son[x])
{
dfs2(v,v);
g[x][0]+=max(f[v][0],f[v][1]);
g[x][1]+=f[v][0];
}
}
}
struct matrix
{
int a[3][3];
matrix()
{
a[1][1]=a[1][2]=a[2][1]=a[2][2]=-Inf;
}
friend matrix operator *(matrix a,matrix b)
{
matrix c;
for (int i=1;i<=2;i++)
for (int j=1;j<=2;j++)
for (int k=1;k<=2;k++)
c.a[i][j]=max(c.a[i][j],a.a[i][k]+b.a[k][j]);
return c;
}
}M[N];
struct SegTree
{
int l[N*4],r[N*4];
matrix f[N*4];
void pushup(int x)
{
f[x]=f[x*2]*f[x*2+1];
}
void build(int x,int ql,int qr)
{
l[x]=ql; r[x]=qr;
if (ql==qr)
{
f[x]=M[rk[ql]];
return;
}
int mid=(l[x]+r[x])>>1;
build(x*2,ql,mid); build(x*2+1,mid+1,qr);
pushup(x);
}
matrix ask(int x,int ql,int qr)
{
if (l[x]==ql && r[x]==qr) return f[x];
int mid=(l[x]+r[x])>>1;
if (qr<=mid) return ask(x*2,ql,qr);
if (ql>mid) return ask(x*2+1,ql,qr);
return ask(x*2,ql,mid)*ask(x*2+1,mid+1,qr);
}
void update(int x,int k)
{
if (l[x]==k && r[x]==k)
{
f[x]=M[rk[k]];
return;
}
int mid=(l[x]+r[x])>>1;
if (k<=mid) update(x*2,k);
else update(x*2+1,k);
pushup(x);
}
}seg;
void Update(int x,int val)
{
M[x].a[2][1]=M[x].a[2][1]-a[x]+val;
a[x]=val;
while (x)
{
matrix last=seg.ask(1,id[top[x]],end[top[x]]);
seg.update(1,id[x]);
matrix now=seg.ask(1,id[top[x]],end[top[x]]);
x=fa[top[x]];
M[x].a[1][1]=M[x].a[1][2]=M[x].a[1][1]-max(last.a[1][1],last.a[2][1])+max(now.a[1][1],now.a[2][1]);
M[x].a[2][1]=M[x].a[2][1]-last.a[1][1]+now.a[1][1];
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&Q);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
tot=0;
dfs1(1,0); dfs2(1,1);
for (int i=1;i<=n;i++)
{
M[i].a[1][1]=M[i].a[1][2]=g[i][0];
M[i].a[2][1]=g[i][1];
M[i].a[2][2]=-Inf;
}
seg.build(1,1,n);
while (Q--)
{
int x,y;
scanf("%d%d",&x,&y);
Update(x,y);
matrix ans=seg.ask(1,id[1],end[1]);
printf("%d\n",max(ans.a[1][1],ans.a[2][1]));
}
return 0;
}