#MNS149. 异或
【题目描述】
一棵 个节点的树,第
个节点的权值是
。
每个节点有一个特殊值,定义为该节点到根节点的路径的权值异或和。
这棵树的权值是每个节点的特殊值之和。
现在你为这棵树确定一个根,使得这棵树的权值最大,并输出这个最大值。
【输入格式】
从
xor.in
中读入数据。
第一行输入一个正整数 。
第二行输入 个正整数
。
接下来 行,每行包含两个正整数
,表示第
个点和第
个点之间连边。
【输出格式】
输出到
xor.out
中。
输出这棵树的最大权值。
【输入样例 1】
3
1 1 2
1 2
2 3
【输出样例 1】
7
【输入样例 2】
4
2 2 0 3
1 2
1 3
2 4
【输出样例 2】
10
【数据范围及约定】
-
对于测试点
:
-
对于测试点
:
-
对于测试点
:
【样例1解释】
以第 个点为根,第
个点的特殊值分别是
,整棵树的权值是
。
以第 个点为根,第
个点的特殊值分别是
,整棵树的权值是
。
以第 个点为根,第
个点的特殊值分别是
,整棵树的权值是
。
【题解】
- 暴力
是枚举根节点,通过的方式逐个计算每个点的特殊值并累加得到整棵树的权值,这样的方法是
的,可以通过前
档得分,但不足与通过最后一档得分。
- 优化
容易发现当根节点确定时,每一位的贡献可分开计算。
首先以为根跑一遍
,设
表示第
个点的子树内的特殊值第
位是
的节点的个数。则第
位对答案的贡献是
。
不妨设第个点的其中一个儿子是第
个点,如果能通过第
个点的
数组计算出以第
个点为根的
数组,则完成了换根的过程。
首先以第个点为根的整棵树的权值需减去以第
个点为根的第
个点的子树的特殊值,剩下的这部分节点的特殊值,第
个点必须经过第
个点才能得到。当第
个点是根时,以第
个点为根的第
个点的子树的特殊值不受其他节点影响,可以保留下来。
考虑第个节点子树外的部分的特殊值会变化成什么,容易发现这部分的特殊值多异或了第
个节点的权值,即
。因此如果
的第
位是
需要将对应位的
个数互换。
综上所述,完成了整个换根的过程。计算出了每个点为根时的整棵树权值,选择最大值输出即可。
换根具体细节实现见代码。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n;
int a[maxn];
vector<int>g[maxn];
int f[maxn][30];
int siz[maxn];
long long ans=0;
void dfs(int fa,int u,int z)
{
siz[u]=1;
int now=(a[u]^z);
for(int i=0;i<30;i++)
if((now>>i)&1)
f[u][i]+=1;
for(auto v:g[u]){
if(v==fa)continue;
dfs(u,v,now);
for(int i=0;i<30;i++)f[u][i]+=f[v][i];
siz[u]+=siz[v];
}
}
void dfs2(int fa,int u,int z)
{
if(u!=1){
for(int i=0;i<30;i++){
int c=0;
int one=0;
if(!(((a[fa]^z)>>i)&1))one+=f[fa][i]-f[u][i];
else one+=f[fa][i]-(siz[u]-f[u][i]);
if((a[u]>>i)&1)one=n-siz[u]-one;
c+=one;
if(!((z>>i)&1))c+=f[u][i];
else c+=siz[u]-f[u][i];
f[u][i]=c;
}
long long val=0;
for(int i=0;i<30;i++)val+=(1ll<<i)*f[u][i];
ans=max(ans,val);
}
for(auto v:g[u]){
if(v==fa)continue;
dfs2(u,v,z^a[u]);
}
}
int main()
{
//freopen("xor.in","r",stdin);
//freopen("xor.out","w",stdout);
std::ios::sync_with_stdio(false),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,1,0);
for(int i=0;i<30;i++)ans+=(1ll<<i)*f[1][i];
dfs2(1,1,0);
cout<<ans<<endl;
return 0;
}