Perfect Security (01字典树&&删除点)
Perfect Security
https://ac.nowcoder.com/acm/problem/112567
题意:
第一行给你一个n
第二行给你n个数字 分别是a[1],a[2].....a[n]。
第三行给你n个数字 分别是b[1],b[2].....b[n]。
问:第三行的序列可自由排列,要求排列之后的顺序 a[1]^b[1],a[2]^b[2]...a[n]^b[n]的字典序最小。
题解:
因为b数组可以自由排列,那么我们把b数组先生成一个字典序。
要想字典序最小,那么根据贪心的思想,我们a[1]要与b中的某个数异或后值最小。
那么我们再遍历一遍a数组每次找出当前能组合的最小值。
因为每次组合后,我们需要删除b中一个元素,那么b已经是字典树了,怎么删?
我们用一个size数组来记录这个数出现的次数,和一个fa数组记录当前结点的父亲结点。
当size[node]=0的时候,这个数字也就没了,就该删除这个结点了。(因为可能会出现重复数组)
递归往上删除,如果当前节点删除,他的父节点只有它一个孩子,那么该父节点也需要删除,重复操作即可!
代码:
/*Keep on going Never give up*/ //#pragma GCC optimize(3,"Ofast","inline") #include<bits/stdc++.h> //#define int long long #define endl '\n' #define Accepted 0 #define AK main() #define I_can signed using namespace std; const int maxn =1e7+10; const int MaxN = 0x3f3f3f3f; const int MinN = 0xc0c0c00c; typedef long long ll; const int inf=0x3f3f3f3f; const ll mod=1e9+7; using namespace std; int trie[maxn][2],sz[maxn],fa[maxn]; int cnt; int a[500000]; void ins(int x){ int node=0; for(int i=31;i>=0;i--){ int t=(x>>i)&1; if(!trie[node][t]){ trie[node][t]=++cnt; fa[cnt]=node; } node=trie[node][t]; } sz[node]++; } int quer(int x){ int res=0,node=0; for(int i=31;i>=0;i--){ int t=(x>>i)&1; if(trie[node][t]){ node=trie[node][t]; } else{ node=trie[node][t^1]; res|=(1<<i); } } if((--sz[node])==0){ while(node){ int f=fa[node],b; if(trie[f][1]==node) b=1; else b=0; trie[f][b] = 0; trie[node][b]=0; if(trie[f][b^1]) break; node=f; } } return res; } signed main() { ios::sync_with_stdio(false); int n; cin>>n; for(int i=0;i<n;i++){ cin>>a[i]; //ins(a[i]); } for(int i=0;i<n;i++){ int x; cin>>x; ins(x); } for(int i=0;i<n;i++){ cout<<quer(a[i])<<" "; } return 0; }
题解 文章被收录于专栏
主要写一些题目的题解