2024杭电多校01——1003树
补题链接
官方题解
补充:
( ∑ u ∈ t r e e i a u ) 2 (\sum_{u \in tree_i} a_u)^2 (∑u∈treeiau)2 = ∑ u ∈ t r e e i a u 2 + 2 ∑ x ∈ t r e e i , y ∈ t r e e i a x ∗ a y = \sum_{u \in tree_i} a_u^{2}+2\sum_{x \in tree_i,y \in tree_i} a_x*a_y =∑u∈treeiau2+2∑x∈treei,y∈treeiax∗ay
可以用树状数组算出 ∑ a u 2 \sum a_u^{2} ∑au2和 ∑ a u \sum a_u ∑au,这样通过上述式子就可以求出 ∑ a x ∗ a y \sum a_x*a_y ∑ax∗ay即原文中的第二行
蒟蒻不太理解后面说的话,只能讲点自己会的,对于一个集合和一个加入的点,会有大于集合元素的贡献和小于集合元素的贡献(贡献就是f(u,v)要我们求和的东西),如何快速求出贡献,可以用三个树状数组去维护,t1代表小于等于x的元素个数,tx代表所有元素的和,txx代表大于x的元素平方和
每次合并答案的时候, ∑ f ( u , v ) = ∑ a [ u ] ≤ a [ x ] f ( x , u ) + ∑ a [ x ] ≤ a [ v f ( x , v ) \sum f(u,v) = \sum_{a[u] \leq a[x]} f(x,u) +\sum_{a[x] \leq a[v} f(x,v) ∑f(u,v)=∑a[u]≤a[x]f(x,u)+∑a[x]≤a[vf(x,v)小于等于x的元素个数存储在t1里,大于x的元素的平方和存储在txx里,要减去的部分全在tx里,这样就可以跑树上启发式合并了
#include<bits/stdc++.h>
using namespace std;
using i64 = unsigned long long;
using i128 = __int128;
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int unsigned long long
const int maxn = 1e6+10;
int n,a[maxn];
vector<int>g[maxn];int son[maxn],siz[maxn],HH;
int f[maxn],ff[maxn],l[maxn],r[maxn],idx=1;void dfs(int x,int fa){siz[x]=1;f[x]=idx;ff[idx] = x;l[x]=idx++;for(auto y:g[x]){if(y==fa) continue;dfs(y,x);siz[x]+=siz[y];if(siz[son[x]]<siz[y]) son[x] = y;}r[x] = idx-1;
}struct bit{i64 tree[maxn];inline int lowbit(int x){return x&(-x);}inline void update(int x,i64 v){for(int i = x;i<maxn;i+=lowbit(i)){tree[i]+=v;}}inline i64 query(int x){i64 res = 0;for(int i = x;i>0;i-=lowbit(i)){res+=tree[i];}return res;}
}t1,tx,txx;i64 add(i64 x){return x*x*t1.query(x)-x*tx.query(maxn-10)+txx.query(maxn-10)-txx.query(max(x,0uLL));
}vector<i64> tmp;
void upd(i64 x){t1.update(x,1uLL);tx.update(x,x);txx.update(x,x*x);tmp.push_back(x);
}void clear(){for(auto x:tmp){t1.update(x,-1);tx.update(x,-1uLL*x);txx.update(x,-1uLL*x*x);}vector<i64>().swap(tmp);//清空tmp省内存
}i64 ans[maxn];
void dsu(int x,int fa,bool op){for(auto y:g[x]){if(y==fa||y==son[x]) continue;dsu(y,x,0);}if(son[x]) dsu(son[x],x,1),HH = son[x],ans[x] = ans[son[x]];for(auto y:g[x]){if(y==fa||y==HH) continue;ans[x]+=ans[y];for(int i = l[y];i<=r[y];++i){ans[x]+=2uLL*add(a[ff[i]]);}for(int i = l[y];i<=r[y];++i){upd(a[ff[i]]);}}ans[x]+=2uLL*add(a[x]);HH=0;if(!op) clear();else upd(a[x]);
}signed main(){ios;cin>>n;for(int i = 1;i<n;++i){int x,y;cin>>x>>y;g[x].push_back(y);g[y].push_back(x);}for(int i = 1;i<=n;++i) cin>>a[i];dfs(1,0);dsu(1,0,1);i64 res = 0;for(int i = 1;i<=n;++i) res=(res^ans[i]);cout<<res<<"\n";return 0;
}
杭电多校的题解好少啊,官方题解又看不懂QAQ。
有没有大佬有不求dfs序的写法,之前的模版都是不用求dfs序的,现在求dfs序感觉怪怪的,对dfs不是很熟悉感觉