【题目地址】
本来应该用动态点分治来做的,复杂度是 O ( n l o g n ) O(nlogn) O(nlogn),但是发现树链剖分好像挺好写的XD,于是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)过了…
题意可以去看原题面。
其实我们数据中询问以1号点为根的,从这里入手,我们先将每个点的子树值的和求出来,记为 s 1 s_1 s1,然后用树剖把它变成序列,然后再记 S 1 ( u ) = ∑ v s 1 ( v ) S_1(u)=\sum_{v}s_1(v) S1(u)=∑vs1(v),其中 v v v为 u u u子树中的点,记 S 2 ( u ) = S 1 ( u ) 2 S_2(u)=S_1(u)^2 S2(u)=S1(u)2,我们树剖后用线段树维护这两个值。
考虑序列上维护权值和和和的平方(读起来有点怪XD)。
首先,以一号点为根的答案就是:
∑ i = 1 n s 1 ( i ) 2 \sum_{i=1}^ns_1(i)^2 i=1∑ns1(i)2
考虑修改操作:
修改一个点 u u u的权值,我们考虑其变化量,假设为 v a l val val,那么会影响到的就是从 1 ∼ u 1\sim u 1∼u这条路上的所有点的答案,首先加上变化量 v a l val val,答案就变成了:
∑ i = 1 n ( s 1 ( i ) + v a l ) 2 = ∑ i = 1 n s 1 ( i ) 2 + 2 × s 1 ( i ) × v a l + v a l 2 = ∑ i = 1 n s 1 ( i ) 2 + 2 × v a l × ∑ i = 1 n s 1 ( i ) + v a l 2 \sum_{i=1}^n(s_1(i)+val)^2=\sum_{i=1}^ns_1(i)^2+2\times s_1(i)\times val + val^2 \\ =\sum_{i=1}^ns_1(i)^2+2\times val\times \sum_{i=1}^ns_1(i)+val^2 i=1∑n(s1(i)+val)2=i=1∑ns1(i)2+2×s1(i)×val+val2=i=1∑ns1(i)2+2×val×i=1∑ns1(i)+val2
所以每次增加的就是 v a l 2 val^2 val2和 2 × v a l × S 1 ( n ) 2\times val\times S_1(n) 2×val×S1(n),用个 l a z y lazy lazy标记,区间修改即可。
现在考虑根不是 1 1 1的情况:
首先记原来根为 1 1 1的答案为 a n s 1 ans_1 ans1,然后我们考虑,当根变成 r r r时,会发生变化的只有 1 ∼ r 1\sim r 1∼r这条路上的点的贡献,所以我们记原来每个点的 s 1 ( i ) s_1(i) s1(i)为 a i a_i ai,换根后的为 b i b_i bi,那么新的答案就为:
∑ i = 1 n s 1 ( i ) 2 − ∑ v ∈ [ 1 ∼ r ] a i 2 + ∑ v ∈ [ 1 ∼ r ] b i 2 = a n s 1 − ∑ v ∈ [ 1 ∼ r ] a i 2 + ∑ v ∈ [ 1 ∼ r ] b i 2 \sum_{i=1}^ns_1(i)^2-\sum_{v\in[1\sim r]}a_i^2+\sum_{v\in[1\sim r]}b_i^2 \\ =ans_1-\sum_{v\in[1\sim r]}a_i^2+\sum_{v\in[1\sim r]}b_i^2 i=1∑ns1(i)2−v∈[1∼r]∑ai2+v∈[1∼r]∑bi2=ans1−v∈[1∼r]∑ai2+v∈[1∼r]∑bi2
然后我们看对于改变后的 b i b_i bi不好求,而 a i a_i ai开始已经预处理出来了,所以我们考虑 b i b_i bi和 a i a_i ai的关系:
可以发现在 1 ∼ r 1\sim r 1∼r这条路上,假如一个点 u u u的下一个点(从1到r的下一个点)为 v v v,那么就有:
a v + b u = a 1 = b r a_v+b_u=a_1=b_r av+bu=a1=br
所以我们令 b u = a 1 − a v b_u=a_1-a_v bu=a1−av,重新编号的话就是 b i = a 1 − a i + 1 b_i=a_1-a_{i+1} bi=a1−ai+1,带回原式,假如 1 ∼ r 1\sim r 1∼r有 k k k个点:
a n s = a n s 1 − ∑ i = 1 k a i 2 + ∑ i = 1 k − 1 ( a 1 − a i + 1 ) 2 + a 1 2 = a n s 1 − ∑ i = 2 k a i 2 + ∑ i = 2 k ( a 1 2 − 2 ⋅ a 1 ⋅ a i + a i 2 ) = a n s 1 − ∑ i = 2 k a i 2 + ( k − 1 ) a 1 2 − 2 ⋅ a 1 ⋅ ∑ i = 2 k a i + ∑ i = 2 k a i 2 = a n s 1 + ( k − 1 ) a 1 2 − 2 ⋅ a 1 ⋅ ∑ i = 2 k a i = a n s 1 + a 1 ( ( k − 1 ) a 1 − 2 ⋅ ∑ i = 2 k a i ) \begin{aligned} ans =&ans_1-\sum_{i=1}^ka_i^2+\sum_{i=1}^{k-1}(a_1-a_{i+1})^2+a_1^2 \\ =&ans_1-\sum_{i=2}^ka_i^2+\sum_{i=2}^{k}(a_1^2-2\cdot a_1\cdot a_i +a_i^2) \\ =&ans_1-\sum_{i=2}^ka_i^2+(k-1)a_1^2-2\cdot a_1\cdot\sum_{i=2}^{k}a_i+\sum_{i=2}^ka_i^2 \\ =&ans_1+(k-1)a_1^2-2\cdot a_1\cdot\sum_{i=2}^ka_i \\ =&ans_1+a_1\left((k-1)a_1-2\cdot\sum_{i=2}^ka_i\right) \end{aligned} ans=====ans1−i=1∑kai2+i=1∑k−1(a1−ai+1)2+a12ans1−i=2∑kai2+i=2∑k(a12−2⋅a1⋅ai+ai2)ans1−i=2∑kai2+(k−1)a12−2⋅a1⋅i=2∑kai+i=2∑kai2ans1+(k−1)a12−2⋅a1⋅i=2∑kaians1+a1((k−1)a1−2⋅i=2∑kai)
而 a i a_i ai又等于 s 1 ( i ) s_1(i) s1(i),所以带入式子,每次算一下就好啦,复杂度 O ( l o g 2 n ) O(log^2n) O(log2n)
用上面线段树维护的两个 S 1 , S 2 S_1,S_2 S1,S2表示,也就是:
S 2 ( 1 ) + S 1 ( 1 ) ⋅ ( k ⋅ S 1 ( 1 ) − 2 ⋅ ∑ i = 2 k s 1 ( i ) ) S_2(1)+S_1(1)\cdot(k\cdot S_1(1)-2\cdot \sum_{i=2}^ks_1(i)) S2(1)+S1(1)⋅(k⋅S1(1)−2⋅i=2∑ks1(i))
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=4e5+10;
int n,m;
int val[M];
struct ss{
int to,last;
ss(){}
ss(int a,int b):to(a),last(b){}
}g[M<<1];
int head[M],cnt;
void add(int a,int b){
g[++cnt]=ss(b,head[a]);head[a]=cnt;
g[++cnt]=ss(a,head[b]);head[b]=cnt;
}
int dep[M],f[M],son[M],sze[M],top[M],num[M],rf[M],tim;
ll sum1[M];
ll Sqr(ll a){return a*a;}
void dfs1(int a){
sze[a]=1;sum1[a]=val[a];
for(int i=head[a];i;i=g[i].last){
if(g[i].to==f[a]) continue;
dep[g[i].to]=dep[a]+1;
f[g[i].to]=a;
dfs1(g[i].to);
sze[a]+=sze[g[i].to];
sum1[a]+=sum1[g[i].to];
if(!son[a]||sze[son[a]]<sze[g[i].to])
son[a]=g[i].to;
}
}
void dfs2(int a,int b){
top[a]=b;rf[num[a]=++tim]=a;
if(!son[a]) return;
dfs2(son[a],b);
for(int i=head[a];i;i=g[i].last){
if(g[i].to==son[a]||g[i].to==f[a]) continue;
dfs2(g[i].to,g[i].to);
}
}
ll S1[M<<2],S2[M<<2],lazy[M<<2],All;
void pushup(int o){
S2[o]=S2[o<<1]+S2[o<<1|1];
S1[o]=S1[o<<1]+S1[o<<1|1];
}
void pushdown(int o,int l,int r,int mid){
if(!lazy[o]) return;
lazy[o<<1]+=lazy[o];
lazy[o<<1|1]+=lazy[o];
S2[o<<1]+=Sqr(lazy[o])*(mid-l+1)+2ll*lazy[o]*S1[o<<1];
S2[o<<1|1]+=Sqr(lazy[o])*(r-mid)+2ll*lazy[o]*S1[o<<1|1];
S1[o<<1]+=(mid-l+1)*lazy[o];
S1[o<<1|1]+=(r-mid)*lazy[o];
lazy[o]=0;
}
void build(int o,int l,int r){
if(l==r){
S1[o]=sum1[rf[l]];
S2[o]=Sqr(S1[o]);
return;
}
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);
}
void update(int o,int l,int r,int L,int R,ll v){
if(L<=l&&r<=R){
S2[o]+=Sqr(v)*(r-l+1)+2ll*v*S1[o];
S1[o]+=(r-l+1)*v;
lazy[o]+=v;
return;
}
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(L<=mid) update(o<<1,l,mid,L,R,v);
if(R>mid) update(o<<1|1,mid+1,r,L,R,v);
pushup(o);
}
void query(int o,int l,int r,int L,int R,ll &a1,ll &a2){
if(L<=l&&r<=R){
a1+=S1[o];a2+=S2[o];
return;
}
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(L<=mid) query(o<<1,l,mid,L,R,a1,a2);
if(R>mid) query(o<<1|1,mid+1,r,L,R,a1,a2);
}
ll Query(int a){
ll ans=0,s1=0,ls=0,k=dep[a];
query(1,1,n,1,n,ls,ans);
while(top[a]!=top[1]){
query(1,1,n,num[top[a]],num[a],s1,ls);
a=f[top[a]];
}
query(1,1,n,num[1]+1,num[a],s1,ls);
return ans+All*(k*All-2ll*s1);
}
void Update(int a,ll b){
b-=val[a];All+=b;val[a]+=b;
while(a){
update(1,1,n,num[top[a]],num[a],b);
a=f[top[a]];
}
}
int a,b,opt;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
add(a,b);
}
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
dfs1(1);
dfs2(1,1);
build(1,1,n);
All=sum1[1];
while(m--){
scanf("%d",&opt);
if(opt==1){
scanf("%d%d",&a,&b);
Update(a,b);
}else{
scanf("%d",&a);
printf("%lld\n",Query(a));
}
}
return 0;
}
- 动态点分治
维护原理差不多,放到点分树上而已。
详细讲解先咕咕咕.
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=2e5+10;
int n,m;
ll vv[M];
struct ss{
int to,last;
ss(){}
ss(int a,int b):to(a),last(b){}
}g[M<<1];
int head[M],cnt;
void add(int a,int b){
g[++cnt]=ss(b,head[a]);head[a]=cnt;
g[++cnt]=ss(a,head[b]);head[b]=cnt;
}
namespace LCA{
int dep[M],top[M],f[M],son[M],sze[M];
void dfs1(int a){
sze[a]=1;
for(int i=head[a];i;i=g[i].last){
if(g[i].to==f[a]) continue;
f[g[i].to]=a;
dep[g[i].to]=dep[a]+1;
dfs1(g[i].to);
sze[a]+=sze[g[i].to];
if(!son[a]||sze[son[a]]<sze[g[i].to]){
son[a]=g[i].to;
}
}
}
void dfs2(int a,int b){
top[a]=b;
if(!son[a]) return;
dfs2(son[a],b);
for(int i=head[a];i;i=g[i].last){
if(g[i].to==son[a]||g[i].to==f[a]) continue;
dfs2(g[i].to,g[i].to);
}
}
int lca(int a,int b){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
a=f[top[a]];
}
if(dep[a]>dep[b])swap(a,b);
return a;
}
int getdep(int a,int b){
return dep[a]+dep[b]-2*dep[lca(a,b)];
}
void init(){
dfs1(1);
dfs2(1,1);
}
}
int root,sum;
int sze[M],son[M],f[M];
bool vis[M];
void getroot(int a,int fa){
son[a]=0;sze[a]=1;
for(int i=head[a];i;i=g[i].last){
if(vis[g[i].to]||g[i].to==fa) continue;
getroot(g[i].to,a);
sze[a]+=sze[g[i].to];
son[a]=max(son[a],sze[g[i].to]);
}
son[a]=max(son[a],sum-sze[a]);
if(son[a]<son[root])root=a;
}
void solve(int a,int b){
f[a]=b;vis[a]=1;
for(int i=head[a];i;i=g[i].last){
if(vis[g[i].to]) continue;
sum=sze[g[i].to];root=0;
getroot(g[i].to,0);
solve(root,a);
}
}
ll val[M],s1[M],s2[M],s[M],All,Now;
void update(int a,ll b){
val[a]+=b;
for(int i=a;f[i];i=f[i]){
int v=LCA::getdep(a,f[i]);
val[f[i]]+=b;
s1[f[i]]+=b*v;
s2[i]+=b*v;
}
}
ll query(int a){
ll now=s1[a];
for(int i=a;f[i];i=f[i]){
int v=LCA::getdep(a,f[i]);
now+=1ll*v*(val[f[i]]-val[i]);
now+=s1[f[i]]-s2[i];
}
return now;
}
void dfs(int a,int b){
s[a]=vv[a];
for(int i=head[a];i;i=g[i].last){
if(g[i].to==b) continue;
dfs(g[i].to,a);
s[a]+=s[g[i].to];
}
Now+=s[a]*(All-s[a]);
}
void Update(int a,ll b){
b-=vv[a];
update(a,b);
All+=b;vv[a]+=b;
Now+=b*query(a);
}
ll Query(int a){return (query(a)+All)*All-Now;}
int a,b,opt;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
add(a,b);
}
for(int i=1;i<=n;i++){scanf("%lld",&vv[i]);All+=vv[i];}
dfs(1,0);
LCA::init();
root=0;son[0]=M;
getroot(1,0);
solve(root,0);
for(int i=1;i<=n;i++)update(i,vv[i]);
while(m--){
scanf("%d",&opt);
if(opt==1){
scanf("%d%d",&a,&b);
Update(a,b);
}else{
scanf("%d",&a);
printf("%lld\n",Query(a));
}
}
return 0;
}