【Luogu3676】小清新数据结构题(动态点分治)

时间:2021-06-16 14:31:38

【Luogu3676】小清新数据结构题(动态点分治)

题面

洛谷

题解

先扯远点,这题我第一次看的时候觉得是一个树链剖分+线段树维护。

做法大概是这样:

我们先以任意一个点为根,把当前点看成是一棵有根树。比方说以\(1\)为根。

那么,在询问以\(p\)为根的时候的答案,我们看看哪些子树发生了变化。

发现真正会产生变化的只有\(1..p\)这条链上的所有点,其它点的贡献和以\(1\)为根时的贡献是一样的。

考虑这条链上的所有点的贡献变成了什么,假设这条链上的所有点分别是\(c_1,c_2...,c_n\)

那么\(c_i\)的子树和是\(\sum Val-\sum _{SubTree c_{i+1}}Val\),

也就是整棵树的所有权值和减去这条链上的那个儿子的子树和。

因为最终的贡献有个平方,所以我们维护子树的\((\sum Val)^2\),\(\sum Val\),

修改的时候把平方式拆开来维护就好了

更丧一点,你可以把\([(\sum val)^2,\sum val , c]\)看成一个矩阵,每次修改相当于一个矩阵乘法

其中\(c=1\)。

这样子就可以用线段树+树链剖分来维护了。

这样应该是对的吧,我没有实践,纯属yy

以上内容都是废话,可以当做没有看见

还是一样,先确定为一棵有根树,

设\(s_i\)表示以\(i\)为根的的子树的权值和,\(w\)为整棵树的权值和。

我们要求的东西是\(\sum_{i=1}^n s_i^2\)

发现\(\sum_{i=1}^n s_i(w-s_i)\)是定值。

证明是这样的,我们考虑一下上述式子是个什么东西,即在任意一个点的子树内和子树外中选择一个点然后求他们的乘积和。

那么,对于任意一对\((u,v)\),他们产生的贡献的次数显然是枚举路径上除了\(lca\)外的任意一个点进行选择,那么路径上的点数是定值,所以上述式子是定值。

那么这就很好办了,\(w\)是很容易维护的,所以我们只需要维护出\(\sum_{i=1}^ns_i\)

就有\(\sum_{i=1}^ns_i^2=w\sum_{i=1}^ns_i-P\),其中\(P\)就是这个定值。

这样子以来,所有的东西都可以利用动态点分治维护即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int n,m,V[MAX];
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
/********************************************************************/
int size[MAX],dfn[MAX],top[MAX],dep[MAX],fa[MAX],tim,hson[MAX];
void dfs1(int u,int ff)
{
fa[u]=ff;size[u]=1;dep[u]=dep[ff]+1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
dfs1(v,u);size[u]+=size[v];
if(size[v]>size[hson[u]])hson[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;
if(hson[u])dfs2(hson[u],tp);
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=fa[u]&&e[i].v!=hson[u])
dfs2(e[i].v,e[i].v);
}
int LCA(int u,int v)
{
while(top[u]^top[v])dep[top[u]]<dep[top[v]]?v=fa[top[v]]:u=fa[top[u]];
return dep[u]<dep[v]?u:v;
}
int Dis(int u,int v){return dep[u]+dep[v]-2*dep[LCA(u,v)];}
/********************************************************************/
bool vis[MAX];
int Fa[MAX],Size,root,mx;
void Getroot(int u,int ff)
{
size[u]=1;int ret=0;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff||vis[v])continue;
Getroot(v,u);size[u]+=size[v];
ret=max(ret,size[v]);
}
ret=max(ret,Size-size[u]);
if(ret<mx)mx=ret,root=u;
}
void DFS(int u,int ff)
{
vis[u]=true;Fa[u]=ff;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(vis[v])continue;
mx=Size=size[v];
Getroot(v,u);DFS(root,u);
}
}
/********************************************************************/
ll P,W;
ll sum[MAX],tf[MAX],num[MAX];
void Modify(int u,int w)
{
num[u]+=w;
for(int i=u;Fa[i];i=Fa[i])
{
int d=Dis(u,Fa[i]);
num[Fa[i]]+=w;sum[Fa[i]]+=1ll*w*d;
tf[i]+=1ll*w*d;
}
}
ll Query(int u)
{
ll ret=sum[u];
for(int i=u;Fa[i];i=Fa[i])
{
int d=Dis(u,Fa[i]);
ret+=1ll*d*(num[Fa[i]]-num[i]);
ret+=sum[Fa[i]]-tf[i];
}
return ret;
}
void dfs(int u,int ff)
{
size[u]=V[u];
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)dfs(e[i].v,u),size[u]+=size[e[i].v];
P+=1ll*size[u]*(W-size[u]);
}
/********************************************************************/
int main()
{
n=read();m=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
Add(u,v);Add(v,u);
}
for(int i=1;i<=n;++i)V[i]=read();
dfs1(1,0);dfs2(1,1);
Size=mx=n;Getroot(1,0);DFS(root,0);
for(int i=1;i<=n;++i)Modify(i,V[i]),W+=V[i];
dfs(1,0);
while(m--)
{
int opt=read(),x=read(),y;
if(opt==1)
{
y=read();Modify(x,y-V[x]);W+=y-V[x];
P+=(y-V[x])*Query(x);V[x]=y;
}
else printf("%lld\n",(Query(x)+W)*W-P);
}
return 0;
}