[LUOGU3676]小清新数据结构题

Posted by Dispwnl on August 20, 2018

题目

题目背景

本题时限2s,内存限制256M

题目描述

在很久很久以前,有一棵n个点的树,每个点有一个点权。

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

(题目不是很好懂,没看太懂的可以看看样例解释)

输入输出格式

输入格式:

第一行两个整数n、q。

接下来n-1行每行两个整数a和b,表示树中a与b之间有一条边,保证给出的边不会重复。

接下来一行n个整数,第i个整数表示第i个点的点权。

接下来q行每行两或三个数,如果第一个数为1,那么接下来有两个数x和y,表示将第x个点的点权修改为y,如果第一个数为2,那么接下来有一个数x,表示询问以x为根时每棵子树点权和的平方和。

输出格式:

对于每个询问输出答案。

输入输出样例

输入样例#1:

4 5
1 2
2 3
2 4
4 3 2 1
2 2
1 1 3
2 3
1 2 4
2 4

输出样例#1:

121
140
194

说明

样例解释

这是一个菊花图,2与1、3、4间有边。

一开始每个点点权分别为4、3、2、1。

第一个询问以2为根,1、3、4子树中都只有本身一个点,2子树中有所有点,那么1、3、4子树中的点权和就分别是自己的点权4、2、1,2子树中的点权和就是4+3+2+1=10, $4^2+2^2+1^2+10^2=121$。

接下来将第一个点点权修改为3,每个点点权分别为3、3、2、1。

第二个询问以3为根,1、4子树中只有自己,2子树中有1、2、4,3子树中有所有点,1、4子树点权和就是自己的点权3、1,2子树点权和就是3+3+1=7,3子树点权和为3+3+2+1=9,$3^2+1^2+7^2+9^2=140$。

接下来把第二个点点权修改为4,每个点点权分别为3、4、2、1。

第三个询问以4为根,1、3子树点权和就是3和2,2子树点权和就是3+4+2=9,4子树点权和为3+4+2+1=10,$3^2+2^2+9^2+10^2=194$。

数据范围

对于10%的数据,$n,q\leq 2000$。

对于40%的数据,$n,q\leq 60000$。

对于另外30%的数据,保证每次询问的根都为1。

对于100%的数据,$1\leq n,q\leq 200000$,$−10\leq $输入的每个点权$\leq 10$。

建议使用输入优化,虽然标程没加读入优化也能过

题解

神™小清新

看数据,里面有$30\%$的数据保证每次询问根都为$1$

如果一个点点权被修改了,可以看成这个点增加了一个值(比如$5\rightarrow 3$可以看成$5+(-2)=3$)

考虑$a$点点值增加了$x$会对答案有什么影响

增加点值肯定影响的是以$(1,a)$这条路径上的点为根的子树的值,$sum_i$为以$i$为根的子树和

$ans_{now}=ans+\sum_{i\in (1,a)}(sum_i+x)^2-\sum_{i\in (1,a)}sum_i^2$

$=ans+length_{(1,a)}x^2+\sum_{i\in (1,a)}sum_i^2+2x\sum_{i\in (1,a)}sum_i-\sum_{i\in (1,a)}sum_i^2$

$=ans+length_{(1,a)}x^2+2x\sum_{i\in (1,a)}sum_i$

可以看出,需要维护的是$(1,a)$上$sum$的和,需要查询和修改

考虑换根操作,假设换的根为$rt$,$S_i$以$rt$为根时以$i$为根的子树和

可以发现换根只影响$(1,a)$上的点,其他点对答案无影响(画图看看)

对于$(1,a)$上的点$i$,如图:

红色部分是$sum_i$,橙色部分是$S_i$,可以发现$sum_i+S_{son_i}=sum_1=S_{rt}$

所以答案变成:

$ans_{rt}=ans+\sum_{i \in (1,rt)}{S_i}^2-\sum_{i \in (1,rt)}{sum_i}^2$

$=ans+{S_{rt}}^2+\sum_{i \in (1,fa_{rt})}{S_i}^2-\sum_{i \in (1,rt)}{sum_i}^2$

$=ans+{sum_1}^2+\sum_{i \in(son_1,rt)}(sum_1-sum_i)^2-\sum_{i\in (1,rt)}{sum_i}^2$

$=ans+(length_{(1,rt)}-1){sum_1}^2-2\times sum_1\sum_{i \in(son(1),rt)}sum_i$

$=ans+sum_1\times ((length_{(1,rt)}-1)sum_1-2\times (\sum_{i \in(1,rt)}sum_i-sum_1))$

$=ans+sum_1\times ((length_{(1,rt)}+1)sum_1-2\times \sum_{i \in(1,rt)}sum_i)$

发现还是需要$(1,a)$上的$sum$的和,还有$sum_1$,修改时维护一下就好了

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define tl (k<<1)
# define tr (k<<1|1)
# define mid (l+r>>1)
# define LL long long
using namespace std;
const int MAX=2e5+1;
struct p{
	int x,lazy;
}s[MAX<<2];
struct o{
	int x,y;
}c[MAX<<1];
int n,q,num,cnt;
LL ans;
int h[MAX],W1[MAX],w[MAX],son[MAX],siz[MAX],id[MAX],fa[MAX],top[MAX],d[MAX],sum[MAX];
int read()
{
	int x=0,f=1;
	char ch=getchar();
	for(;!isdigit(ch);f=(ch=='-')?-1:1,ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x*f;
}
void add(int x,int y)
{
	c[++num]=(o){h[x],y},h[x]=num;
	c[++num]=(o){h[y],x},h[y]=num;
}
void dfs(int x,int f)
{
	fa[x]=f,d[x]=d[f]+1,siz[x]=1,sum[x]=w[x];
	for(int i=h[x];i;i=c[i].x)
	  {
	  	int y=c[i].y;
	  	if(y==f) continue;
	  	dfs(y,x);
	  	siz[x]+=siz[y],sum[x]+=sum[y];
	  	if(siz[y]>siz[son[x]]) son[x]=y;
	  }
}
void dfs1(int x,int tp)
{
	ans+=1ll*sum[x]*sum[x],top[x]=tp,W1[id[x]=++cnt]=sum[x];
	if(son[x]) dfs1(son[x],tp);
	else return;
	for(int i=h[x];i;i=c[i].x)
	  {
	  	int y=c[i].y;
	  	if(y==fa[x]||y==son[x]) continue;
	  	dfs1(y,y);
	  }
}
void pus(int k)
{
	s[k].x=s[tl].x+s[tr].x;
}
void build(int l,int r,int k)
{
	if(l==r)
	{
		s[k].x=W1[l];
		return;
	}
	build(l,mid,tl),build(mid+1,r,tr);
	pus(k);
}
void down(int l,int r,int k)
{
	if(!s[k].lazy) return;
	s[tl].lazy+=s[k].lazy,s[tr].lazy+=s[k].lazy;
	s[tl].x+=(mid-l+1)*s[k].lazy,s[tr].x+=(r-mid)*s[k].lazy;
	s[k].lazy=0;
}
void change(int l,int r,int k,int L,int R,int dis)
{
	if(l==L&&r==R)
	{
		s[k].x+=(r-l+1)*dis,s[k].lazy+=dis;
		return;
	}
	down(l,r,k);
	if(R<=mid) change(l,mid,tl,L,R,dis);
	else if(L>mid) change(mid+1,r,tr,L,R,dis);
	else change(l,mid,tl,L,mid,dis),change(mid+1,r,tr,mid+1,R,dis);
	pus(k);
}
void CHANGE(int x,int dis)
{
	while(top[1]!=top[x])
	change(1,n,1,id[top[x]],id[x],dis),x=fa[top[x]];
	change(1,n,1,1,id[x],dis);
}
LL ask(int l,int r,int k,int L,int R)
{
	if(l==L&&r==R) return s[k].x;
	down(l,r,k);
	if(R<=mid) return ask(l,mid,tl,L,R);
	if(L>mid) return ask(mid+1,r,tr,L,R);
	return ask(l,mid,tl,L,mid)+ask(mid+1,r,tr,mid+1,R);
}
LL ASK(int x)
{
	LL ans=0;
	while(top[1]!=top[x])
	ans+=ask(1,n,1,id[top[x]],id[x]),x=fa[top[x]];
	return ans+ask(1,n,1,1,id[x]);
}
int main()
{
	n=read(),q=read();
	for(int i=1;i<n;++i)
	  add(read(),read());
	for(int i=1;i<=n;++i)
	  w[i]=read();
	dfs(1,0),dfs1(1,1),build(1,n,1);
	int Sum=sum[1];
	for(int i=1;i<=q;++i)
	  {
	  	int op=read(),x=read();
	  	if(op==1)
	  	{
	  		int dis=read(),Dis=dis-w[x];
	  		w[x]=dis,Sum+=Dis;
	  		ans+=1ll*Dis*Dis*(d[x])+2ll*Dis*ASK(x),CHANGE(x,Dis);
		}
		else if(op==2) printf("%lld\n",ans+1ll*Sum*((d[x]+1)*Sum-2ll*ASK(x)));
	  }
	return 0;
}