本文同步发布于我的网站
Problem
给一棵根为 1 的有根树,点 i i i 具有一个权值 A i A_i Ai 。
定义一个点对的值 f ( u , v ) = max ( A u , A v ) × ∣ A u − A v ∣ f(u, v)=\max \left(A_u, A_v\right) \times\left|A_u-A_v\right| f(u,v)=max(Au,Av)×∣Au−Av∣ 。
你需要对于每个节点 i i i ,计算 a n s i = ∑ u ∈ subtree ( i ) , v ∈ subtree ( i ) f ( u , v ) a n s_i=\sum_{u \in \operatorname{subtree}(i), v \in \operatorname{subtree}(i)} f(u, v) ansi=∑u∈subtree(i),v∈subtree(i)f(u,v) ,其中 subtree ( i ) \operatorname{subtree}(i) subtree(i) 表示 i i i 的子树。
请你输出 ⊕ ( a n s i m o d 2 64 ) \oplus\left(a n s_i \bmod 2^{64}\right) ⊕(ansimod264) ,其中 ⊕ \oplus ⊕ 表示 XOR。
n ≤ 5 × 1 0 5 , 1 ≤ A i ≤ 1 0 6 n \leq 5 \times 10^5, 1 \leq A_i \leq 10^6 n≤5×105,1≤Ai≤106
Solution
先来愉快的推式子。
其实 max ( A u , A v ) × ∣ A u − A v ∣ \max \left(A_u, A_v\right) \times\left|A_u-A_v\right| max(Au,Av)×∣Au−Av∣ 其实就是 max 2 − max ⋅ min \max^2-\max \cdot \min max2−max⋅min,这两部分可以分开思考。
对于 max ⋅ min \max\cdot\min max⋅min,其实就是在 i i i 的子树内任选两个点 u , v ∈ subtree ( i ) u,v\in \operatorname{subtree}(i) u,v∈subtree(i) 相乘
∑
u
∑
v
A
u
×
A
v
=
∑
u
A
u
∑
v
A
v
=
(
∑
u
A
u
)
2
\begin{align} &\sum_{u} \sum_{v } A_u\times A_v\\ =&\sum_{u }A_u\sum_{v}A_v\\ =&(\sum_{u}A_u)^2 \end{align}
==u∑v∑Au×Avu∑Auv∑Av(u∑Au)2
对于
max
2
\max ^2
max2,也就是
∑
u
,
v
∈
subtree
(
i
)
(
max
(
A
u
,
A
v
)
)
2
\sum_{u,v\in \operatorname{subtree}(i)}(\max(A_u,A_v))^2
∑u,v∈subtree(i)(max(Au,Av))2,我们需要思考子树合并的情况。
假设我们已经计算了节点 u u u 的所有子节点的子树的内部信息, v v v 是 u u u 的某个儿子,此时我们需要计算
- u u u 与 subtree ( v ) \operatorname{subtree}(v) subtree(v) 之间的贡献
- subtree ( v i ) \operatorname{subtree}(v_i) subtree(vi) 与 subtree ( v j ) \operatorname{subtree}(v_j) subtree(vj) 之间的贡献(即跨点 u u u 的两点之间的贡献)
我们按照以下方式合并的同时计算贡献(以下步骤来自于题解)
- subtree ( u ) \operatorname{subtree}(u) subtree(u) 初始为 { u } \{u\} {u} 。
- 计算 subtree ( v ) \operatorname{subtree}(v) subtree(v) 和当前 subtree ( u ) \operatorname{subtree}(u) subtree(u) 之间点对的答案。(跨越 u u u 节点的部分)。
- 把 subtree ( v ) \operatorname{subtree}(v) subtree(v) 子树内的答案直接累加。(不跨越 u u u 节点的部分)。
- subtree ( u ) ← subtree ( u ) + subtree ( v ) \operatorname{subtree}(u) \leftarrow \operatorname{subtree}(u)+\operatorname{subtree}(v) subtree(u)←subtree(u)+subtree(v) (将 v v v 的子树加入到 u u u 中)。
我们需要维护两个变量:一个子树内的权值出现次数 c n t cnt cnt 与权值平方和 s u m sum sum。
当前子树 subtree ( u ) \operatorname{subtree}(u) subtree(u) 内加入一个权重为 w w w 的点,对于答案贡献多少呢?
-
对于 subtree ( u ) \operatorname{subtree}(u) subtree(u) 中每个权值小于 w w w 的点,贡献 1 × w 2 1\times w^2 1×w2,总计 2 × ∑ i = 1 w − 1 c n t i × w 2 2\times\sum_{i=1}^{w-1} cnt_i\times w^2 2×∑i=1w−1cnti×w2。
-
对于 subtree ( u ) \operatorname{subtree}(u) subtree(u) 中每个权值大于等于 w w w 的点(权重为 w ′ w^\prime w′),贡献 1 × w ′ 2 1\times {w^\prime}^2 1×w′2,总计 2 × ∑ i = w 1 0 6 s u m i 2\times\sum_{i=w}^{10^6}sum_i 2×∑i=w106sumi
对于每个节点,我们开一颗线段树。初始时,每个节点的线段树只包含其本身点权。计算完某个点所有儿子的 a n s ans ans 之后,我们将所有儿子的线段树合并到其自己上,同时计算贡献。
Code
#define N 500010
#define M 1000000
ULL a[N];
int n;
namespace Tree
{
int head[N],nxt[N*2],ver[N*2],f[N];
int cnt;
void insert(int x,int y)
{
nxt[++cnt]=head[x];
head[x]=cnt;
ver[cnt]=y;
}
};
using Tree::insert;
using Tree::head;
using Tree::nxt;
using Tree::ver;
namespace Seg
{
struct Node
{
int ls,rs,l,r;
ULL sum,cnt;
#define ls(x) a[x].ls
#define rs(x) a[x].rs
#define l(x) a[x].l
#define r(x) a[x].r
#define sum(x) a[x].sum
#define cnt(x) a[x].cnt
}a[N*40];
int cnt;
int root[N];
int new_node(int l,int r)
{
cnt++;
l(cnt)=l;
r(cnt)=r;
return cnt;
}
void add(int &p,int x)
{
debug
if(p==0) p=new_node(1,M);
if(l(p)==r(p))
{
debug
sum(p)+=(ULL)(x)*x;
cnt(p)++;
return;
}
int mid=(l(p)+r(p))/2;
if(x<=mid)
{
if(!ls(p)) ls(p)=new_node(l(p),mid);
add(ls(p),x);
}
else
{
if(!rs(p)) rs(p)=new_node(mid+1,r(p));
add(rs(p),x);
}
cnt(p)=cnt(ls(p))+cnt(rs(p));
sum(p)=sum(ls(p))+sum(rs(p));
}
int merge(int x,int y,ULL &ans)
{
if(!x) return y;
if(!y) return x;
if(l(x)==r(x))
{
ans+=2*cnt(x)*sum(y);
sum(x)+=sum(y);
cnt(x)+=cnt(y);
return x;
}
sum(x)+=sum(y);
cnt(x)+=cnt(y);
ans+=2*cnt(ls(x))*sum(rs(y));
ans+=2*cnt(ls(y))*sum(rs(x));
ls(x)=merge(ls(x),ls(y),ans);
rs(x)=merge(rs(x),rs(y),ans);
return x;
}
};
using Seg::add;
using Seg::merge;
using Seg::root;
ULL sq[N],ans[N],sum[N];
void dfs(int x,int f)
{
sum[x]=a[x];
sq[x]=a[x]*a[x];
add(root[x],a[x]);
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(y==f) continue;
dfs(y,x);
sum[x]+=sum[y];
sq[x]+=sq[y];
root[x]=merge(root[x],root[y],sq[x]);
}
ans[x]=sq[x]-sum[x]*sum[x];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);
int t=1;
// cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
Tree::insert(x,y);
Tree::insert(y,x);
}
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
dfs(1,0);
ULL out=0;
for(int i=1;i<=n;i++)
{
out^=ans[i];
// cout<<ans[i]<<" ";
}
cout<<out<<endl;
}
return 0;
}