前言
我是被逼的
不打还真不会打
模版题
干啥的
树上最大独立集?
动态改点权?
NOIP2018D2T3?
…
玩法
以模版题为例
先写出
d
p
dp
dp方程,常用的
f
i
,
0
/
1
f_{i,0/1}
fi,0/1表示当前点选了/没选的最大方案
转移有
f
i
,
0
=
∑
m
a
x
(
f
s
o
n
,
0
,
f
s
o
n
,
1
)
f_{i,0}=\sum max(f_{son,0},f_{son,1})
fi,0=∑max(fson,0,fson,1)
f
i
,
1
=
c
a
l
i
+
∑
f
s
o
n
,
0
f_{i,1}=cal_i+\sum f_{son,0}
fi,1=cali+∑fson,0
设
l
d
p
i
,
0
/
1
ldp_{i,0/1}
ldpi,0/1表示仅考虑轻子树,自己选/=不选的最大方案
先改变矩阵的定义,原本的乘变成加,求和变成取max
按照倍增floyd的方式,可证这个也是满足结合律的
常规套路写成矩阵
[
l
d
p
f
a
,
0
l
d
p
f
a
,
0
l
d
p
f
a
,
1
−
I
N
F
]
∗
[
f
i
,
0
f
i
,
1
]
=
[
f
f
a
,
0
f
f
a
,
1
]
\left[ \begin{matrix}ldp_{fa,0}&ldp_{fa,0}\\ldp_{fa,1}&-INF \end{matrix} \right] *\left[ \begin{matrix} f_{i,0}\\f_{i,1}\end{matrix}\right ]= \left[ \begin{matrix}f_{fa,0}\\f_{fa,1}\end{matrix}\right]
[ldpfa,0ldpfa,1ldpfa,0−INF]∗[fi,0fi,1]=[ffa,0ffa,1]
注意这是一条重链上的转移,在重链上越靠上的点的矩阵乘在最前面
常规做法就用树剖优化可以做到
8
n
l
o
g
2
n
8nlog^2n
8nlog2n
全局平衡二叉树
据说这玩意在07年的论文就有了?
类似一个静态的LCT
先说建法
先把树常规剖分一下,然后对重链建一个非完美的BST
对于每个点,我们设他的权为
∑
s
i
z
[
l
i
g
h
t
s
o
n
]
\sum{siz[lightson]}
∑siz[lightson],即为轻儿子的子树大小
对于每条重链,我们找到其带权重心
for(int x=l;;x=preson[x])
{
sum+=lsiz[x];
if(x==r)break;
}
for(int x=l,num=lsiz[x];;x=preson[x],num+=lsiz[x])
{
if(num*2>=sum){u=x;break;}
if(x==r)break;
}
是一个类似这样的过程
然后递归两边继续建树
然后每个点有至多两个实儿子,就是他两边的中心
按照LCT的玩法,还有轻边这种
轻边就是原树上的轻儿子连向父亲的边…这个记录一下就好了
虽然对于一条重链可能会建成一个非常左倾/右倾的二叉树
不慌,分析一下复杂度
当我们跳轻边的时候,子树大小至少增长一倍
当我们跳BST上的边的时候,轻子树大小同样至少增加一倍
那么感性认知一下,这棵树的树高大概是log级别的…
然后我们就可以愉快的开展非法操作了
对于每个点,记录两个矩阵
m
1
m1
m1表示他的子树的矩阵乘积
m
2
m2
m2表示他的轻子树与他自己构成的
l
d
p
i
,
0
/
1
ldp_{i,0/1}
ldpi,0/1的权值
更新的话有一个小小的骚操作可以减点常数qwq…
一定要非常注意矩阵乘的顺序,左儿子先乘自己再乘右儿子
更新就一直跳上去更新就好了
注意跳BST上的边的时候仅需要更新自己子树的
m
1
m1
m1,否则要先把自己给父亲的贡献去掉,再更新子树
m
1
m1
m1,再把贡献打回到父亲的
m
2
m2
m2上
这个玩意还能询问子树
d
p
dp
dp值,询问这个的话你就要找到这个点然后去他BST上的右孩子找到他的
m
1
m1
m1再乘上我的
m
2
m2
m2,注意不能直接用自己的
m
1
m1
m1来算答案
当然一般都是询问
r
o
o
t
root
root的答案,此时直接回答就好
代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int stack[20];
inline void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(!x){putchar('0');return;}
int top=0;
while(x)stack[++top]=x%10,x/=10;
while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=100005;
struct egde{int x,y,next;}a[2*MAXN];int len,last[MAXN];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
struct matrix
{
int m[2][2];
matrix(){m[0][0]=m[1][1]=0;m[0][1]=m[1][0]=-(1<<30);}
friend matrix operator *(matrix u,matrix v)
{
matrix ret;
for(int i=0;i<=1;i++)for(int j=0;j<=1;j++)for(int k=0;k<=1;k++)
ret.m[i][k]=max(ret.m[i][k],u.m[i][j]+v.m[j][k]);
return ret;
}
int getmx(){return max(max(m[0][0],m[0][1]),max(m[1][0],m[1][1]));}
};
int preson[MAXN],up[MAXN],siz[MAXN],lsiz[MAXN];
void pre_tree_node(int x)
{
siz[x]=1;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=up[x])
{
up[y]=x;
pre_tree_node(y);
if(siz[y]>siz[preson[x]])preson[x]=y;
siz[x]+=siz[y];
}
}
lsiz[x]=siz[x]-siz[preson[x]];
}
int cal[MAXN],si[MAXN],root;
int n,m;
struct BST
{
int son[MAXN][2],fa[MAXN];
matrix m1[MAXN],m2[MAXN];//记录子树乘积 记录自己信息(轻子树)
void updata(int x)
{
m1[x]=m1[son[x][0]]*m2[x];
m1[x]=m1[x]*m1[son[x][1]];
}//注意矩阵乘的方法
bool isroot(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
int buildtree(int l,int r)//父亲->儿子
{
if(l>r||!l)return 0;
int u=0,sum=0;
for(int x=l;;x=preson[x])
{
sum+=lsiz[x];
if(x==r)break;
}
for(int x=l,num=lsiz[x];;x=preson[x],num+=lsiz[x])
{
if(num*2>=sum){u=x;break;}
if(x==r)break;
}
son[u][0]=buildtree(l,up[u]);son[u][1]=buildtree(preson[u],r);
if(son[u][0])fa[son[u][0]]=u;
if(son[u][1])fa[son[u][1]]=u;
return u;
}
int init()
{
int ret;
for(int i=1;i<=n;i++)m1[i].m[0][0]=m1[i].m[0][1]=m1[i].m[1][0]=0,m1[i].m[1][1]=-(1<<30);
for(int i=1;i<=n;i++)fa[i]=up[i],m2[i].m[0][0]=m2[i].m[1][0]=m2[i].m[0][1]=0,m2[i].m[1][1]=-(1<<30);
for(int i=1;i<=n;i++)if(preson[up[i]]!=i)
{
int L=i,R;
for(R=i;preson[R];R=preson[R]);
int u=buildtree(L,R);
fa[u]=up[i];
if(!up[i])ret=u;
}
return ret;
}
void modify(int x,int W)
{
m2[x].m[1][0]+=W-cal[x];cal[x]=W;
for(;x!=0;x=fa[x])
{
if(isroot(x)&&fa[x])
{
m2[fa[x]].m[0][0]-=max(m1[x].m[0][0],m1[x].m[1][0]);m2[fa[x]].m[0][1]=m2[fa[x]].m[0][0];
m2[fa[x]].m[1][0]-=m1[x].m[0][0];
updata(x);
m2[fa[x]].m[0][0]+=max(m1[x].m[0][0],m1[x].m[1][0]);m2[fa[x]].m[0][1]=m2[fa[x]].m[0][0];
m2[fa[x]].m[1][0]+=m1[x].m[0][0];
}
else updata(x);
}
}
}bst;
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)si[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
ins(x,y);ins(y,x);
}
pre_tree_node(1);
root=bst.init();
for(int i=1;i<=n;i++)
bst.modify(i,si[i]);
// int lastans=0;
while(m--)
{
int x=read(),y=read();
bst.modify(x,y);
pr2(bst.m1[root].getmx());
}
return 0;
}