传送门

Description

有一棵\(n\)个点的树,每个点有一个点权。

现在有\(q\)次操作,每次操作是修改一个点的点权或指定一个点,询问以这个点为根时每棵子树点权和的平方和。

Solution

我们设\(Sum=\sum_{i=1}^{n} w_i\),\(s_i\)表示\(i\)子树的权值和

发现不管根是哪个节点,\(W=\sum_{i=1}^n s_i(Sum-s_i)\)都是一个定值

因为它相当于对于每条边连接的两个联通块的”点权和的积“的和

所以,我们要求的\(\sum_{i=1}^{n} s_i^2=Sum*(\sum_{i=1}^n s_i)-W\)

考虑怎么计算\(calc(root)=\sum_{i=1}^{n} s_i\)

发现其实上就是\(Sum+\sum_{i=1}^n dis(root,i)*val_i\)

可以用点分树来维护

考虑怎么计算\(W\)

对于每次修改\(val_i+=delta\),就有\(W+=\sum_{j=1}^n val_j*dis(i,j)*delta=delta*calc(i)\)

发现这道题本质和幻想乡战略游戏是一样的

可是把原来的代码交上去,就\(TLE\)了,只好考虑重构

不妨把过程重新理一遍:

  1. 点分树有一个性质,对于树上的两个点,它们的\(lca\)一定在两点在原树上的简单路径上

    因此,要求两个点的实际距离,可以通过分别计算它们到\(lca\)的实际距离求和得到

  2. 考虑以上性质,我们可以设:

    • \(vsum_i\)表示\(i\)在点分树上的子树内的权值和
    • \(dis1_i\)表示 \(\sum_{j} dis(i,j)*val_j\),\(j\)是\(i\)在点分树上的子树内的节点
    • \(dis2_i\)表示 \(\sum_{j} dis(par_i,j)*val_j\)
    • \(par_i\)是\(i\)在点分树上的父亲
  3. \(dis1_i=\sum_{son} dis2_son\),\(son\)是\(i\)在点分树上的儿子

  4. \(calc(i)\)其实上就是枚举\(lca\)

    \(lca=i\)时,和为\(dis1_i\)

    \((\sum_{lca=par_j} dis1_{par_j}-dis2_j)\)算的是外围节点到\(lca\)的和

    \(dis(i,par_j)*(vsum_{par_j}-vsum_j))\)算的是它们到\(i\)的和

我们发现,求距离的部分其实不需要像之前那样全部记下来,可以用\(RMQ\)求树上距离

具体来说,和\(RMQ\)求\(LCA\)差不多,只不过维护的最小值不是欧拉序,而是到根路径的长

Code 

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define reg register
#define int ll inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
const int MN=2e5+5; int n,m;
struct edge{int to,w,nex;}e[MN<<2];int en,hr[MN];
void ins(int x,int y,int w,int *h){e[++en]=(edge){y,w,h[x]};h[x]=en;}
int Val[MN],O,SUM; struct Tree
{
int Hr[MN],ind,st[MN<<2][21],dep[MN],pos[MN<<1],lg[MN<<2],_siz[MN]; inline void Ins(int x,int y,int w){ins(x,y,w,Hr);ins(y,x,w,Hr);} int dis(int x,int y)
{
if(pos[x]>pos[y]) std::swap(x,y);
reg int k=lg[pos[y]-pos[x]+1];
return dep[x]+dep[y]-2*min(st[pos[x]][k],st[pos[y]-(1<<k)+1][k]);
}
void dfs(int x,int fa=0)
{
st[pos[x]=++ind][0]=dep[x];reg int i;
for(i=Hr[x];i;i=e[i].nex)if(e[i].to^fa)
dep[e[i].to]=dep[x]+e[i].w,dfs(e[i].to,x),st[++ind][0]=dep[x];
}
void get_O(int x=1,int fa=0)
{
reg int i;_siz[x]=Val[x];
for(i=Hr[x];i;i=e[i].nex)if(e[i].to^fa)
get_O(e[i].to,x),_siz[x]+=_siz[e[i].to];
O+=1ll*(SUM-_siz[x])*_siz[x];
}
inline void pre_work()
{
reg int i,j;get_O();dfs(1);
for(lg[0]=-1,i=1;i<(MN<<2);i++)lg[i]=lg[i>>1]+1;
for(j=1;j<20;++j)for(i=1;i+(1<<j)-1<=ind&&i<=ind;++i)
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
} }T; int sum,rt,mx[MN],vis[MN],par[MN],cnt,siz[MN];
ll dis1[MN],dis2[MN],sumv[MN]; void getrt(int x,int fa)
{
siz[x]=1;mx[x]=0;reg int i;
for(i=T.Hr[x];i;i=e[i].nex)if(!vis[e[i].to]&&(e[i].to!=fa))
getrt(e[i].to,x),siz[x]+=siz[e[i].to],mx[x]=max(mx[x],siz[e[i].to]);
mx[x]=max(mx[x],sum-siz[x]);
if(mx[x]<mx[rt]) rt=x;
} void _work(int x,int fa)
{
vis[x]=1;par[x]=fa;reg int i;
for(i=T.Hr[x];i;i=e[i].nex)if(!vis[e[i].to])
mx[rt=0]=sum=siz[e[i].to],getrt(e[i].to,0),ins(x,rt,0,hr),_work(rt,x);
} void pre_work()
{
sum=mx[rt=0]=n;getrt(1,0);
int tmp=rt;_work(rt,0);rt=tmp;
} inline void Modify(int x,int val)
{
sumv[x]+=val;reg int i,dis;
for(i=x;par[i];i=par[i])
{
dis=T.dis(par[i],x);
dis1[par[i]]+=dis*val;
dis2[i]+=dis*val;
sumv[par[i]]+=val;
}
} inline ll calc(int x)
{
ll ans=dis1[x];reg int i,dis;
for(i=x;par[i];i=par[i])
{
dis=T.dis(par[i],x);
ans+=dis1[par[i]]-dis2[i];
ans+=dis*(sumv[par[i]]-sumv[i]);
}
return ans;
} signed main()
{
n=read();m=read();
reg int i,x,y;
for(i=1;i<n;i++) x=read(),y=read(),T.Ins(x,y,1);
pre_work();
for(i=1;i<=n;++i) SUM+=Val[i]=read();
T.pre_work();
for(i=1;i<=n;++i) Modify(i,Val[i]); while(m--)
{
reg int opt=read();
if(opt==1)
{
x=read();y=read()-Val[x];Modify(x,y);
SUM+=y;O+=y*calc(x);Val[x]+=y;
}
else printf("%lld\n",SUM*(calc(read())+SUM)-O);
}
return 0;
}

Blog来自PaperCloud,未经允许,请勿转载,TKS!

lp3676 小清新数据结构题的更多相关文章

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

    [Luogu3676]小清新数据结构题(动态点分治) 题面 洛谷 题解 先扯远点,这题我第一次看的时候觉得是一个树链剖分+线段树维护. 做法大概是这样: 我们先以任意一个点为根,把当前点看成是一棵有根 ...

  2. [Luogu3676]小清新数据结构题

    题面戳我 题意:给一棵树,树上有点权,每次操作为修改一个点的点权,或者是询问以某个点为根时,每棵子树(以每个点为根,就有n棵子树)点权和的平方和. \(n\le2*10^5\),保证答案在long l ...

  3. [P3676]小清新数据结构题

    Description: 给你一棵树,每次询问以一个点为根时所有子树点权和的平方和 带修改 Hint: \(n\le 2*10^5\) Solution: 这题只要推出式子就很简单了 如果不换根这个平 ...

  4. 洛谷P3676 小清新数据结构题(动态点分治+树链剖分)

    传送门 感觉这题做下来心态有点崩……$RMQ$求$LCA$没有树剖快我可以理解为是常数太大……然而我明明用了自以为不会退化的点分然而为什么比会退化的点分跑得反而更慢啊啊啊啊~~~ 先膜一波zsy大佬 ...

  5. [luogu3676] 小清新数据结构题 [树链剖分+线段树]

    题面 传送门 思路 本来以为这道题可以LCT维护子树信息直接做的,后来发现这样会因为splay形态改变影响子树权值平方和,是splay本身的局限性导致的 所以只能另辟蹊径 首先,我们考虑询问点都在1的 ...

  6. 洛谷 P3676 小清新数据结构题

    https://www.luogu.org/problemnew/show/P3676 这题被我当成动态dp去做了,码了4k,搞了一个换根的动态dp #include<cstdio> #i ...

  7. 洛谷 P3676 - 小清新数据结构题(动态点分治)

    洛谷题面传送门 题目名称好评(实在是太清新了呢) 首先考虑探究这个"换根操作"有什么性质.我们考虑在换根前后虽然每个点的子树会变,但整棵树的形态不会边,换句话说,割掉每条边后,得到 ...

  8. 洛谷P3676 小清新数据结构题 [动态点分治]

    传送门 思路 这思路好妙啊! 首先很多人都会想到推式子之后树链剖分+线段树,但这样不够优美,不喜欢. 脑洞大开想到这样一个式子: \[ \sum_{x} sum_x(All-sum_x) \] 其中\ ...

  9. Luogu3676 小清新数据结构题 动态点分治

    传送门 换根类型的统计问题动态点分治都是很好做的. 设所有点的点权和为$sum$ 首先,我们先不考虑求$\sum\limits_i s_i^2$,先考虑如何在换根的情况下求$\sum\limits_i ...

随机推荐

  1. Python定义点击右上角关闭按钮事件

    Python定义点击右上角关闭按钮事件(Python defines the event of clicking the close button in the upper right corner) ...

  2. CentOS - 查看操作系统版本

    cat /etc/redhat-release 参考: https://www.cnblogs.com/baby123/p/6962398.html

  3. jenkins节点添加

    https://blog.csdn.net/bbwangj/article/details/81203381

  4. Spring Cloud原理详解

    概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留在对Spring Cloud功能使用的层面,其底层的很多原理,很多人可能并不知晓 ...

  5. [ipsec][strongswan]strongswan源码分析--(零)引子

    目录 strongswan sa 资料 编译 启动 进程信息 结构 架构图与插件 配置运行 传统配置方法 新的配置方法 其他配置方法 详细的配置文档 配置示例 用法 加密库 libgmp libcry ...

  6. 修改bash终端命令行颜色

    要修改linux终端命令行颜色,我们需要用到PS1,PS1是Linux终端用户的一个环境变量,用来说明命令行提示符的设置.在终端输入命令:#set,即可在输出中找到关于PS1的定义如下: PS1='[ ...

  7. Buffer Latch Timeout的解析

    [问题描述] 我们可能会在数据库的错误日志里,发现这么一条信息: A time-out occurred while waiting for buffer latch -- type 4, bp 00 ...

  8. Sharding-JDBC(三)3.1.0版本实践

    目录 一.Sharding-JDBC依赖 二.代码实践 三.源码分析 在上一篇博文中,介绍了Sharding-JDBC的分片策略.分片键和分片算法的基本概念,以及2.0.3版本可以支持和无法支持的使用 ...

  9. python开发笔记-python-numpy

    一.Numpy概念 Numpy(Numerical Python的简称)是Python科学计算的基础包.它提供了以下功能:  除了为Python提供快速的数组处理能力,Numpy在数据分析方面还有另外 ...

  10. javaweb-servlet获取给定文件在服务器上的绝对路径的方法

    1.通过ServletContext获取 在tomcat5,6,7版本中我们可以通过ServletContext来获取给定文件在服务器上的绝对路径. ServletContext context = ...