标算点分治,蒟蒻树链剖分。
原题可以戳。由于表达能力过弱,所以自己不再复述题面。
可能有一大堆 Σ Σ 。。
然而我并不会那么数学的符号。。。
所以全凭脑算。
在不考虑换根与修改的情况下,只要理解了题意,求解就是dfs。但由于发现修改是一个点一个点修改的,所以每次要针对一个点的修改来算出与修改前的贡献差。所以说要针对出一个点来算贡献。如果我们是按一个一个点来算贡献的话,就可以通过枚举他所在的子树来算贡献。发现了一个平方,手推式子不可避免。
如果一个点改变了,考虑他所在的一个子树,把平方式子拆开,就形如 a2+b2+c2+...+2ab+2bc+2ac+... a 2 + b 2 + c 2 + . . . + 2 a b + 2 b c + 2 a c + . . . 显然有的与a(假设a发生变化)无关的式子,这些式子的值肯定不变.除了一个平方项,其他都可以提一个 2a 2 a 出来,剩下的就是一个求和。这样针对每一个点,在以1为根的情况下,维护他的子树的权值和(包括自己)。这样,用线段树(或者树状数组),求出每一个包括a的子树的权值和减去几个a再乘一个 2a 2 a .
如果根变了,那么,先设现在的根为 u u ,设 a1,a2... a 1 , a 2 . . . 为在以1为根的情况下,子树i的权值和为 ai a i , b1,b2... b 1 , b 2 . . . 是以 u u 为根的情况。以1为根的答案为 ans a n s ,u为根的情况答案为 Ans A n s ,然后推一波式子,主要是要把 bi b i 消掉,因为没有维护 bi b i .
#include
#define LL long long
using namespace std;
namespace zjy_io{
inline char gc(){
static char buf[1<<6],*p1=buf,*p2=buf;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2)?EOF:*p1++;
}
template <class T>
inline void read(T&data){
data=0;register int caa=1;register char ch=0;
while((ch!='-')&&(ch<'0'||ch>'9'))ch=gc();
ch=='-'?caa=-1,ch=gc():caa=1;
while(ch<='9'&&ch>='0'){
data=(data<<3)+(data<<1)+(ch^48);
ch=gc();
}
data=data*caa;
}
}
using namespace zjy_io;
const int _ = 2e5+1e2;
struct edge{
int to,nt;
}e[_<<1];
int s[_],head[_],n,q,deep[_],top[_],son[_],size[_],fa[_],dfn[_],fdfn[_],cnt;
LL sum[_],tree[_<<2],lazy[_<<2];
LL ans1,ALL;
inline void update(register int qp){
tree[qp]=tree[qp<<1]+tree[qp<<1|1];return;
}
inline void pushdown(register int qp,register int le,register int ri){
if(lazy[qp]){
register int mid= (le+ri)>>1,len1=mid+1-le,len2=ri-mid;
lazy[qp<<1]+=lazy[qp],tree[qp<<1]+=lazy[qp]*len1;
lazy[qp<<1|1]+=lazy[qp],tree[qp<<1|1]+=lazy[qp]*len2;
lazy[qp]=0;
}
return;
}
void modify(const int l,const int r,register LL zh,int L,int R,register int qp){
if(L>=l&&R<=r){tree[qp]+=zh*(R-L+1);lazy[qp]+=zh;return;}
register int mid=(L+R)>>1;
if(l<=mid)modify(l,r,zh,L,mid,qp<<1);
if(r>mid)modify(l,r,zh,mid+1,R,qp<<1|1);
update(qp);return;
}
LL Query(const int l,const int r,register int L,register int R,int qp){
if(L>=l&&R<=r){
return tree[qp];
}
pushdown(qp,L,R);
register int mid= (L+R)>>1;
register LL kkk=0;
if(l<=mid)kkk+=Query(l,r,L,mid,qp<<1);
if(r>mid)return kkk+Query(l,r,mid+1,R,qp<<1|1);
return kkk;
}
inline LL pre_query(register int a){
if(a==1)return ans1;
//int gnt=0;
register LL delta=0;
register int Begin=a,End;
while(Begin){
/*if(a==3){++gnt;if(gnt>5)exit(0);
cout<
End=top[Begin];
delta=delta+2LL*Query(dfn[End],dfn[Begin],1,n,1)*ALL;
Begin=fa[End];
}
delta=1LL*(deep[a]-deep[1]+2)*(ALL)*(ALL)-delta;
return ans1+delta;
}
void add(register int a,register int b){e[++cnt].to=a,e[cnt].nt=head[b],head[b]=cnt;}
inline void pre_fix(register int a,register int b){
register LL delta=0;
register int Pre=s[a],Now=b;s[a]=b;ALL=ALL+Now-Pre;
delta=delta+1LL*deep[a]*(1LL*Now*Now-1LL*Pre*Pre);
register int Begin=a;
while(Begin){
register int End=top[Begin];
register LL dist = ( Query(dfn[End],dfn[Begin],1,n,1) - (deep[Begin]-deep[End]+1)*Pre)*(Now-Pre)*2;
delta=delta+dist;
modify(dfn[End],dfn[Begin],Now-Pre,1,n,1);
Begin=fa[End];
}
ans1+=delta;
}
void dfsI(register int now,register int ff){
fa[now]=ff,deep[now]=deep[ff]+1,sum[now]=s[now];
size[now]=1;register int mx=0;
for(register int i=head[now];i;i=e[i].nt){
if(e[i].to==ff)continue;
dfsI(e[i].to,now);
size[now]+=size[e[i].to];
if(mxreturn ;
}
void dfsII(register int now,register int topf){
dfn[now]=++cnt,fdfn[cnt]=now;
top[now]=topf;
modify(cnt,cnt,sum[now],1,n,1);
if(son[now])
dfsII(son[now],topf);
for(register int i=head[now];i;i=e[i].nt){
if(e[i].to==fa[now]||e[i].to==son[now])continue;
dfsII(e[i].to,e[i].to);
}
return;
}
int main(){
read(n);read(q);
for(register int i=1,a,b;ifor(register int i=1;i<=n;++i)read(s[i]),ALL+=s[i];
dfsI(1,0);cnt=0;
dfsII(1,1);
while(q--){
register int ki,a,b;
read(ki);
if(ki==1){
read(a),read(b);
pre_fix(a,b);
}
else {
read(a);
printf("%lld\n",pre_query(a));
}
}
}