你最愿意做的哪件事,才是你的天赋所在

0%

异或最小生成树

异或最小生成树

异或最小生成树就是每个点都有一个权值,而两个点之间连接的边的权值是两个点的异或值,求这个点集合的最小生成树

CF888G

思路

我们会用到Boruvka算法,用上了分治的思想来求最小生成树,但是异或最小生成树呢,则还需要一颗trie树,因为它能够log的查询异或的最小值。

简单解释一下异或最小生成树的步骤

  1. 对给定的点值排序
  2. 递归进入子树,从小的子树向大的树合并点集合
  3. 左子树即当前层为1(或0也可以)的点值插入trie树中
  4. 在trie树种查询右子树中异或左子树的最小值
  5. 清空trie树
  6. 向上递归

例子详解

样例1

5

1 2 3 4 5

1.拆成二进制看

image-20200728220715024

分为了区间[1,3] , [4,5] ,[1,3] 又根据第二位分为[1] , [2,3]。

2.[1]区间的贡献为0,因为不需要连边,[2,3]区间的边的最小贡献为2^3=1。ans+=1,

3.合并[1,3]区间,选择[2,3]之中的一个数去与1异或找到最小值(此处需要trie树,加入答案,ans+=1^3

4.合并[4,5]区间,ans+=4^5

5.合并[1,3],[4,5]区间,这里跟3操作一样,把[1,2,3]插入trie树中,然后枚举[4,5]查询异或最小值即可。ans+=1^5

答案为ans=8

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5+10;
ll ans;
int trie[N*30][2],tot,n,a[N];
//在trie中插入值
void insert(int x){
int rt = 0;
for(int i=29;i>=0;i--){
int now = (x>>i)&1;
if(!trie[rt][now])trie[rt][now]=++tot;
rt=trie[rt][now];
}
}
int Search(int x){
int ans = 0,rt=0;
for(int i=29;i>=0;i--){
int now = (x>>i)&1;
if(trie[rt][now]){
rt = trie[rt][now];
}else{
rt = trie[rt][now^1];
ans|=(1<<i);
}
}
return ans;
}
void dfs(int l,int r,int dep){
// printf("%d %d %d\n",l,r,dep);
if(dep==-1||l>=r)return ;
int mid = l-1;
//找到左边集合的边界位置
while(mid<r&&((a[mid+1]>>dep)&1)==0)mid++;
dfs(l,mid,dep-1);
dfs(mid+1,r,dep-1);
if(mid==l-1||mid==r)return ;
//在字典树中插入左边集合
for(int i=l;i<=mid;i++){
insert(a[i]);
}
//枚举右边的数字,寻找最小的异或值
int tmp = INT_MAX;
for(int i=mid+1;i<=r;i++){
tmp = min(tmp,Search(a[i]));
}
ans+=(ll)tmp;
printf("%d %d %d\n",l,r,tmp);
for(int i=0;i<=tot;i++){
trie[i][0]=trie[i][1]=0;
}
tot=0;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+1+n);
dfs(1,n,29);
printf("%lld\n",ans);
}

5670B

image-20200728221301079

思路

先dfs给每个点赋上一个权值,然后跑一遍异或最小生成树即可

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mk make_pair
const int N = 100010;
ll ans;
int trie[N*30][2],tot,n;
vector<pair<int,int>>g[N];
int val[N];
void add(int u,int v,int w){
g[u].push_back(mk(v,w));
}
void insert(int x){
int rt=0;
for(int i=29;i>=0;i--){
int now = (x>>i)&1;
if(!trie[rt][now])trie[rt][now]=++tot;
rt=trie[rt][now];
}
}
int search(int x){
int res = 0,rt=0;
for(int i=29;i>=0;i--){
int now = (x>>i)&1;
if(trie[rt][now]){
rt = trie[rt][now];
}else{
rt=trie[rt][now^1];
res|=(1<<i);
}
}
return res;
}
void solve(int l,int r,int dep){
if(dep==-1||l>=r)return ;
int mid = l-1;
while(mid<r&&((val[mid+1]>>dep)&1)==0)mid++;
solve(l,mid,dep-1);
solve(mid+1,r,dep-1);
if(mid==l-1||mid==r)return;
for(int i=l;i<=mid;i++){
insert(val[i]);
}
int tmp = INT_MAX;
for(int i=mid+1;i<=r;i++){
tmp = min(tmp,search(val[i]));
}
ans+=(ll)tmp;
for(int i=0;i<=tot;i++){
trie[i][0]=trie[i][1]=0;
}
tot=0;
}
void dfs(int rt,int fa){
for(auto tmp : g[rt]){
if(tmp.first==fa)continue;
val[tmp.first]=val[rt]^tmp.second;
dfs(tmp.first,rt);
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<n;i++){
int s,t,v;
scanf("%d %d %d",&s,&t,&v);
s++;
t++;
add(s,t,v);
add(t,s,v);
}
dfs(1,-1);
sort(val+1,val+n+1);
solve(1,n,29);
printf("%lld\n",ans);
}
-------------你最愿意做的哪件事才是你的天赋所在-------------