存在一棵无向连通树,树中有编号从 0
到 n - 1
的 n
个节点, 以及 n - 1
条边。
给你一个下标从 0 开始的整数数组 nums
,长度为 n
,其中 nums[i]
表示第 i
个节点的值。另给你一个二维整数数组 edges
,长度为 n - 1
,其中 edges[i] = [ai, bi]
表示树中存在一条位于节点 ai
和 bi
之间的边。
删除树中两条 不同 的边以形成三个连通组件。对于一种删除边方案,定义如下步骤以计算其分数:
- 分别获取三个组件 每个 组件中所有节点值的异或值。
- 最大 异或值和 最小 异或值的 差值 就是这一种删除边方案的分数。
- 例如,三个组件的节点值分别是:
[4,5,7]
、[1,9]
和[3,3,3]
。三个异或值分别是4 ^ 5 ^ 7 = 6
、1 ^ 9 = 8
和3 ^ 3 ^ 3 = 3
。最大异或值是8
,最小异或值是3
,分数是8 - 3 = 5
。
返回在给定树上执行任意删除边方案可能的 最小 分数。
示例 1:
输入:nums = [1,5,5,4,11], edges = [[0,1],[1,2],[1,3],[3,4]] 输出:9 解释:上图展示了一种删除边方案。 - 第 1 个组件的节点是 [1,3,4] ,值是 [5,4,11] 。异或值是 5 ^ 4 ^ 11 = 10 。 - 第 2 个组件的节点是 [0] ,值是 [1] 。异或值是 1 = 1 。 - 第 3 个组件的节点是 [2] ,值是 [5] 。异或值是 5 = 5 。 分数是最大异或值和最小异或值的差值,10 - 1 = 9 。 可以证明不存在分数比 9 小的删除边方案。
示例 2:
输入:nums = [5,5,2,4,4,2], edges = [[0,1],[1,2],[5,2],[4,3],[1,3]] 输出:0 解释:上图展示了一种删除边方案。 - 第 1 个组件的节点是 [3,4] ,值是 [4,4] 。异或值是 4 ^ 4 = 0 。 - 第 2 个组件的节点是 [1,0] ,值是 [5,5] 。异或值是 5 ^ 5 = 0 。 - 第 3 个组件的节点是 [2,5] ,值是 [2,2] 。异或值是 2 ^ 2 = 0 。 分数是最大异或值和最小异或值的差值,0 - 0 = 0 。 无法获得比 0 更小的分数 0 。
提示:
n == nums.length
3 <= n <= 1000
1 <= nums[i] <= 108
edges.length == n - 1
edges[i].length == 2
0 <= ai, bi < n
ai != bi
edges
表示一棵有效的树
思路:
树形dp+倍增LCA
设xor[u]是以u为根节点的子树的异或和,我们跑一遍树形dp(dfs形式即可)即可统计;
由于数据量只有<=1000,我们再枚举删除那两条边(1000*1000),求出从总树中删除那两个子树,这两个子树可能存在包含关系,可能不相互包含;
1)不相互包含:
aa=xor[u]为以u为跟的子树的异或和,bb=xor[v]为以v为根节点的子树的异或和,则第三个连通块异或和为:cc=xor[1]^xor[u]^xor[v];
所以三个连通块的异或和分别为aa,bb,cc;
当前方案分数:max(aa,bb,cc)-min(aa,bb,vv);
2)相互包含:
假设以u为跟的子树包含以v为根的子树;
则以v为根的子树异或和:aa=xor[v];
以u为根的子树除去以v为根的子树剩余结点所形成的连通块异或和:bb=xor[u]^xor[v];
以1为根的子树(总树)除去以u为根的子树剩余结点所形成的连通块异或和:cc=xor[1]^xor[u];
所以三个连通块的异或和分别为aa,bb,cc;
当前方案分数:max(aa,bb,cc)-min(aa,bb,vv);
那么如何判断u,v子树相互包含呢?
使用LCA:
如果lca(u,v)==u,说明u包含v;
如果lca(u,v)==v,说明v包含u;
否则u,v互不包含;
时间复杂度:O(20*n^2);
AC Code:
struct Edge{
int u,v,next;
};
class Solution {
public:
int fa[1001];
int head[1001];
Edge e[2002];
int val[1001];
int cnt;
int n,m;
int xxor[1001];
int dep[1001];
int bz[1001][11];
void add_edge(int u,int v){
e[++cnt].next=head[u];
head[u]=cnt;
e[cnt].u=u;
e[cnt].v=v;
}
void dfs(int u,int ff){
xxor[u]=val[u];
for(int i=head[u];i!=0;i=e[i].next){
int v=e[i].v;
if(v==ff)continue;
fa[v]=u;
dep[v]=dep[u]+1;
dfs(v,u);
xxor[u]^=xxor[v];
}
}
int lca(int u,int v){
if(dep[u]<dep[v])swap(u,v);
for(int i=10;i>=0;--i){
if(dep[bz[u][i]]>=dep[v]){
u=bz[u][i];
}
}
if(u==v)return u;
for(int i=10;i>=0;--i){
if(bz[u][i]!=bz[v][i]){
u=bz[u][i];
v=bz[v][i];
}
}
return fa[u];
}
int minimumScore(vector<int>& nums, vector<vector<int>>& edges) {
cnt=0;
n=nums.size();
m=edges.size();
for(int i=1;i<=n;++i){
val[i]=nums[i-1];
}
for(int i=0;i<m;++i){
int u=1+edges[i][0];
int v=1+edges[i][1];
add_edge(u,v);
add_edge(v,u);
}
fa[1]=0;
dep[1]=1;
dfs(1,0);
for(int i=1;i<=n;++i){
bz[i][0]=fa[i];
}
for(int i=1;i<=10;++i){
for(int j=1;j<=n;++j){
bz[j][i]=bz[bz[j][i-1]][i-1];
}
}
int res=100000000;
int aa,bb,cc;
for(int i=0;i<m;++i){
for(int j=i+1;j<m;++j){
int son1=1+edges[i][1];
int fa1=1+edges[i][0];
if(dep[son1]<dep[fa1])swap(fa1,son1);
int son2=1+edges[j][1];
int fa2=1+edges[j][0];
if(dep[son2]<dep[fa2])swap(fa2,son2);
int lca_ab=lca(son1,son2);
if(lca_ab==son1){
bb=xxor[son2];
aa=xxor[son1]^bb;
cc=xxor[1]^xxor[son1];
}else if(lca_ab==son2){
aa=xxor[son1];
bb=xxor[son2]^aa;
cc=xxor[1]^xxor[son2];
}else{
aa=xxor[son1];
bb=xxor[son2];
cc=xxor[1]^aa^bb;
}
int mx=max(aa,bb);
int mi=min(aa,bb);
if(cc>mx)mx=cc;
if(cc<mi)mi=cc;
res=min(res,mx-mi);
//printf("i:%d j:%d son1:%d xorson1:%d son2:%d xorson2:%d xorroot:%d mx:%d mi:%d nowres:%d\n",i,j,son1,aa,son2,bb,cc,mx,mi,mx-mi);
}
}
return res;
}
};
/*
[29,29,23,32,17]
[[3,1],[2,3],[4,1],[0,4]]
*/