斜率优化学记笔记
简要题意:给定一棵 \(n\) 个点的树,点带点权。有 \(m\) 次操作,每次操作给定 \(x,y\) 表示修改点 \(x\) 的权值为 \(y\)。你需要在每次操作之后求出这棵树的最大权独立集的权值大小。
最大权独立集:选若干个点,满足两两之间没有边相连且这些点的权值总和最大。
考虑不带修改操作时如何求答案。
令\(f_{i,0}\)表示不选点\(i\)的最大独立集,\(f_{i,1}\)表示选点\(i\)的最大独立集,有:
\[\begin{cases}f_{i,0}=\sum\limits_{son}{max(f_{son,0},f_{son,1})}\\f_{i,1}=\sum\limits_{son}{f_{son,0}}\end{cases}
\]
答案是:\(max(f_{root,1},f_{root,0})\)
考虑带修改操作:
考虑这棵树进行树链剖分。
令\(g_{i,0}\)表示不选择\(i\)且只允许选择\(i\)的轻儿子所在子树的最大答案,\(g_{i,1}\)表示不考虑\(son_i\)的情况下选择\(i\)的最大答案,\(son_i\)表示\(i\)的重儿子
假设已经已经知道\(g_{i,0}\)和\(g_{i,1}\),有:
\[\begin{cases}f_{i,0}=max(f_{son_i,0},f_{son_i,1})+g_{i,0}\\f_{i,1}=f_{son_i,0}+g_{i,1}\end{cases}
\]
特别的,当\(i\)为叶子节点时有:
\[\begin{cases}f_{i,0}=g_{i,0}\\f_{i,1}=g_{i,1}\end{cases}
\]
答案是:\(max(f_{root,1},f_{root,0})\)
考虑矩阵乘法。
定义矩阵乘法\(A*B=C\)为:
\[C_{i,j}=\max_{k=1}^n(A_{i,k}+B_{k,j})
\]
构造矩阵:
\[\begin{bmatrix}g_{i,0}&g_{i,0}\\g_{i,1}&-\infty\end{bmatrix}*\begin{bmatrix}f_{son_i,0}\\f_{son_i,1}\end{bmatrix}=\begin{bmatrix}f_{i,0}\\f_{i,1}\end{bmatrix}
\]
只需在树链剖分进行dfs2处理轻儿子时初始化\(g_{i,0}\)和\(g_{i,1}\),修改\(i\)的点权时修改\(g_{i,1}\)和所有把\(i\)看成轻儿子的点,这些可以在树链剖分求答案时顺便修改
因为每条重链的末尾都是叶子节点,所以答案为\(root\)到\(i\)所在重链末尾的矩阵乘法后的取\(max\),用线段树实现,时间复杂度\(O(log^2n)\)。
上代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+50,INF=1e16;
ll n,m,u,v,a[N];
ll F[N][2];
vector<ll> e[N];
struct jgt
{
ll a[2][2];
}G[N],tr[N*4];
jgt operator * (const jgt t1,const jgt t2)
{
jgt t={-INF,-INF,-INF,-INF};
for(ll i=0;i<2;i++)
for(ll j=0;j<2;j++)
for(ll k=0;k<2;k++)
t.a[i][j]=max(t.a[i][j],t1.a[i][k]+t2.a[k][j]);
return t;
}
ll siz[N],fa[N],son[N];
void dfs1(ll wz,ll last)
{
fa[wz]=last;
siz[wz]=1;
ll gs=-1;
for(ll i=0;i<e[wz].size();i++)
{
ll j=e[wz][i];
if(j==last) continue;
dfs1(j,wz);
siz[wz]+=siz[j];
if(siz[j]>gs) gs=siz[j],son[wz]=j;
}
}
ll top[N],end1[N],id[N],yl[N],cnt;
void dfs2(ll wz,ll topf)
{
id[wz]=++cnt;
yl[cnt]=wz;
top[wz]=topf;
G[wz].a[0][0]=G[wz].a[0][1]=0;
G[wz].a[1][0]=a[wz];
G[wz].a[1][1]=-INF;
F[wz][0]=0;
F[wz][1]=a[wz];
if(!son[wz])
{
end1[wz]=wz;
return ;
}
dfs2(son[wz],topf);
end1[wz]=end1[son[wz]];
F[wz][0]+=max(F[son[wz]][0],F[son[wz]][1]);
F[wz][1]+=F[son[wz]][0];
for(ll i=0;i<e[wz].size();i++)
{
ll j=e[wz][i];
if(j==son[wz]||j==fa[wz]) continue;
dfs2(j,j);
F[wz][0]+=max(F[j][0],F[j][1]);
F[wz][1]+=F[j][0];
G[wz].a[0][0]+=max(F[j][0],F[j][1]);
G[wz].a[0][1]=G[wz].a[0][0];
G[wz].a[1][0]+=F[j][0];
}
}
void BT(ll wz,ll l,ll r)
{
if(l==r)
{
tr[wz]=G[yl[l]];
return ;
}
ll mid=(l+r)/2;
BT(wz*2,l,mid);
BT(wz*2+1,mid+1,r);
tr[wz]=tr[wz*2]*tr[wz*2+1];
}
void gai(ll wz,ll l,ll r,ll md)
{
if(l==r)
{
tr[wz]=G[yl[l]];
return ;
}
ll mid=(l+r)/2;
if(md<=mid) gai(wz*2,l,mid,md);
else gai(wz*2+1,mid+1,r,md);
tr[wz]=tr[wz*2]*tr[wz*2+1];
}
jgt query(ll wz,ll l,ll r,ll le,ll ri)
{
if(le<=l&&ri>=r) return tr[wz];
ll mid=(l+r)/2;
if(le<=mid&&ri>mid) return query(wz*2,l,mid,le,ri)*query(wz*2+1,mid+1,r,le,ri);
if(le<=mid) return query(wz*2,l,mid,le,ri);
return query(wz*2+1,mid+1,r,le,ri);
}
ll updQ(ll y,ll val)
{
G[y].a[1][0]+=val-a[y];
a[y]=val;
jgt A,B;
while(top[y]!=1)
{
A=query(1,1,n,id[top[y]],id[end1[y]]);
gai(1,1,n,id[y]);
B=query(1,1,n,id[top[y]],id[end1[y]]);
y=fa[top[y]];
G[y].a[0][0]+=max(B.a[0][0],B.a[1][0])-max(A.a[0][0],A.a[1][0]);
G[y].a[0][1]=G[y].a[0][0];
G[y].a[1][0]+=B.a[0][0]-A.a[0][0];
}
gai(1,1,n,id[y]);
A=query(1,1,n,id[top[y]],id[end1[y]]);
return max(A.a[0][0],A.a[1][0]);
}
int main()
{
scanf("%lld %lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(ll i=1;i<n;i++)
{
scanf("%lld %lld",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1,0);
dfs2(1,1);
BT(1,1,n);
for(ll i=1;i<=m;i++)
{
scanf("%lld %lld",&u,&v);
printf("%lld\n",updQ(u,v));
}
return 0;
}