题目大意
给定一棵n个点的树,点带点权。
有m次操作,每次操作给定x,y,表示修改点x的权值为y。
你需要在每次操作之后求出这棵树的最大权独立集的权值大小。
题解
如果不带修改操作,正常的DP式:
dp[u][1]dp[u][1]dp[u][1]表示当前结点保证选择,这个结点的子树独立集最大权值。
dp[u][0]dp[u][0]dp[u][0]表示当前结点不选择,这个结点的子树独立集最大权值。
设u的儿子结点为v
dp[u][0]=∑vmax(dp[v][0],dp[v][1])dp[u][0]=\sum\limits_vmax(dp[v][0],dp[v][1])dp[u][0]=v∑max(dp[v][0],dp[v][1])
dp[u][1]=V[u]+∑vdp[v][0]dp[u][1]=V[u]+\sum\limits_vdp[v][0]dp[u][1]=V[u]+v∑dp[v][0]
对这棵树进行树链剖分,设置轻儿子和重链等信息
将dp修改一下,设ldp[u][0/1]表示只更新轻儿子的情况
ldp[u][0]=∑vmax(dp[v][0],dp[v][1])ldp[u][0]=\sum\limits_vmax(dp[v][0],dp[v][1])ldp[u][0]=v∑max(dp[v][0],dp[v][1])
ldp[u][1]=V[u]+∑vdp[v][0]ldp[u][1]=V[u]+\sum\limits_vdp[v][0]ldp[u][1]=V[u]+v∑dp[v][0]
将一条重链用一个序列表示,dp[i][0/1]表示序列第i个的dp值,则
dp[i][0]=ldp[i][0]+max(dp[i+1][0],dp[i+1][1])dp[i][0]=ldp[i][0]+max(dp[i+1][0],dp[i+1][1])dp[i][0]=ldp[i][0]+max(dp[i+1][0],dp[i+1][1])
dp[i][1]=ldp[i][1]+dp[i+1][0]dp[i][1]=ldp[i][1]+dp[i+1][0]dp[i][1]=ldp[i][1]+dp[i+1][0]
于是对于一条重链上的dp,就可以使用矩阵乘法。
[dp[i][0]dp[i][1]]=[ldp[i][0]ldp[i][0]ldp[i][1]−INF]×[dp[i+1][0]dp[i+1][1]]
\left[
\begin{matrix}
dp[i][0]\\dp[i][1]
\end{matrix}\right]=
\left[
\begin{matrix}
ldp[i][0]&ldp[i][0]\\
ldp[i][1]&-INF
\end{matrix}
\right]\times
\left[
\begin{matrix}
dp[i+1][0]\\dp[i+1][1]
\end{matrix}
\right]
[dp[i][0]dp[i][1]]=[ldp[i][0]ldp[i][1]ldp[i][0]−INF]×[dp[i+1][0]dp[i+1][1]]
矩阵乘法满足结合律,可以用线段树维护重链上区间的矩阵乘法的积。
修改及正常的单点修改。
但这里有更快的方法——全局平衡二叉树。
类似于LCT,LCT的复杂度为 O(nlogn)O(nlogn)O(nlogn),但它实际上也类似树链剖分,分为很多重链,重链上使用O(logn)O(logn)O(logn)的数据结构。但是经过玄学的势能分析,LCT总深度期望为lognlognlogn。
全局平衡二叉树也是类似的思想,对树链剖分后的重链,不用线段树,用二叉搜索树,利用一些巧妙的建树方法,使得每个重链的二叉搜索树连起来后,总深度期望lognlognlogn。
对于一条重链,设每个结点的权值为它的轻儿子子树大小之和+1,然后找到这条重链的带权重心(即重心两边的树大小尽量接近),以这个重心为根,递归的往重心左边的重链,重心右边的重链建二叉搜索树。
每一个结点,维护它的二叉搜索树子树的矩阵乘积,修改操作即从被修改的结点,一路往上更新即可。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100005,INF=0x3F3F3F3F;
struct Edge
{
int v;
Edge *nxt;
};
struct Graph
{
Edge *adj[MAXN],edges[MAXN*2],*ed_it;
Graph(){ed_it=edges;}
void AddEdge(int u,int v)
{
ed_it->v=v;
ed_it->nxt=adj[u];
adj[u]=ed_it++;
}
};
struct Matrix
{
int A[2][2];
Matrix()
{A[0][0]=A[0][1]=A[1][0]=A[1][1]=-INF;}
int* operator [] (int i)
{return A[i];}
const int * operator [] (int i)const
{return A[i];}
void unit()
{A[0][0]=A[1][1]=0;A[0][1]=A[1][0]=-INF;}
Matrix operator * (const Matrix &B)const
{
Matrix res;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
res[i][j]=max(res[i][j],A[i][k]+B[k][j]);
return res;
}
int dp(int i)
{return max(A[i][0],A[i][1]);}
};
struct Node
{
Matrix dp,pro;
Node *son[2],*fa;
bool isRoot()
{return fa==NULL||(fa->son[0]!=this&&fa->son[1]!=this);}
void SetChild(Node *v,int d)
{
son[d]=v;
v->fa=this;
}
};
int n,m,V[MAXN];
int siz[MAXN],hv[MAXN],fa[MAXN];
Graph G;
void dfs(int u)
{
siz[u]=1;
int mx=0;
for(Edge *e=G.adj[u];e;e=e->nxt)
{
int v=e->v;
if(v!=fa[u])
{
fa[v]=u;
dfs(v);
siz[u]+=siz[v];
if(siz[v]>mx)
mx=siz[v],hv[u]=v;
}
}
}
namespace BST
{
Node nd[MAXN],*root;
int ID(Node *u){return u-nd;}
void HPushUp(Node *u)
{
u->pro.unit();
if(u->son[0])
u->pro=u->pro*u->son[0]->pro;
u->pro=u->pro*u->dp;
if(u->son[1])
u->pro=u->pro*u->son[1]->pro;
}
void LPushUp(Node *u,Node *v)
{
u->dp[0][0]+=max(v->pro.dp(0),v->pro.dp(1));
u->dp[1][0]+=v->pro.dp(0);
u->dp[0][1]=u->dp[0][0];
}
int sum[MAXN],stk[MAXN],tp;
Node *heavyBuild(int L,int R)
{
Node *res;
int mn=sum[R]-sum[L];
int mid=L;
for(int i=L;i<=R;i++)
if(mn>max(sum[i-1]-sum[L-1],sum[R]-sum[i]))
mn=max(sum[i-1]-sum[L-1],sum[R]-sum[i]),mid=i;
res=&nd[stk[mid]];
if(mid>L)
res->SetChild(heavyBuild(L,mid-1),0);
if(mid<R)
res->SetChild(heavyBuild(mid+1,R),1);
HPushUp(res);
return res;
}
Node *Build(int u)
{
for(int x=u;x;x=hv[x])
{
nd[x].dp[0][0]=nd[x].dp[0][1]=0;
nd[x].dp[1][0]=V[x];nd[x].dp[1][1]=-INF;
for(Edge *e=G.adj[x];e;e=e->nxt)
if(e->v!=fa[x]&&e->v!=hv[x])
{
Node *v=Build(e->v);
v->fa=&nd[x];
LPushUp(&nd[x],v);
}
}
tp=0;
for(int x=u;x;x=hv[x])
{
stk[++tp]=x;
sum[tp]=sum[tp-1]+siz[x]-siz[hv[x]];
}
return heavyBuild(1,tp);
}
void Modify(int x,int y)
{
Node *u=&nd[x];
u->dp[1][0]+=y-V[x];
V[x]=y;
while(u)
{
if(u->isRoot()&&u->fa!=NULL)
{
Node *v=u->fa;
v->dp[0][0]-=max(u->pro.dp(0),u->pro.dp(1));
v->dp[1][0]-=u->pro.dp(0);
HPushUp(u);
v->dp[0][0]+=max(u->pro.dp(0),u->pro.dp(1));
v->dp[1][0]+=u->pro.dp(0);
v->dp[0][1]=v->dp[0][0];
}
else
HPushUp(u);
u=u->fa;
}
}
int Ans()
{return max(root->pro.dp(0),root->pro.dp(1));}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&V[i]);
for(int i=1,u,v;i<n;i++)
{
scanf("%d%d",&u,&v);
G.AddEdge(u,v);
G.AddEdge(v,u);
}
dfs(1);
BST::root=BST::Build(1);
for(int i=1,x,y;i<=m;i++)
{
scanf("%d%d",&x,&y);
BST::Modify(x,y);
printf("%d\n",BST::Ans());
}
return 0;
}