斜率优化学记笔记

传送门

简要题意:给定一棵 \(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;
}
posted @ 2023-08-23 11:03  傻阙的缺  阅读(13)  评论(0)    收藏  举报