题目描述
给定整数m以及n个数字A1, A2, …, An,将数列A中所有元素两两异或,共能得到n(n-1)/2个结果。请求出这些结果中大于m的有多少个。
输入
第一行包含两个整数n, m。
第二行给出n个整数A1, A2, …, An。
样例输入
3 10
6 5 10
输出
输出仅包括一行,即所求的答案。
样例输出
2
一看到这道题开始想用暴力求解O(n^2),毫不意外超时了
后来看了大神的解答,利用Trie树,主要思想如下:
1,使用字典树(TrieTree)从高位到低位建立字典
2,再使用每个元素依次去字典中查对应高位异或结果。
3,若m对应位置为1, 则当前元素在该位的异或也必须为1
4,若m对应位置为0,则加上与当前元素异或结果为1的元素个数
5,将所有元素查找后的结果相加,然后再除以2,就是最终的结果。
#include<iostream>
using namespace std;
const int N=100000;
int a[N];
struct Trie
{
int count;
Trie * next[2];//一个节点有两个分支0,1
Trie()
{
count = 0;
next[0] = NULL;
next[1] = NULL;
}
};
void insert(Trie * root, int num)
{
Trie * p = root;
for(int i = 31;i >=0;i--)
{
int temp = (num>>i)&1;
if(p->next[temp] == NULL)
p->next[temp] = new Trie();
p = p->next[temp];
p->count++;
}
}
long long find(Trie * root, int m, int data)
{
Trie * p = root;
int x, y;
long long result =0;
for(int i = 31;i >=0;i--)
{
x = (data>>i)&1;
y = (m>>i)&1;
if(y == 1)//如果m的位为1,走向与x异或为1的分支
p = p->next[x^1];
else
{
if(p->next[x^1]!=NULL)//如果m的位为0,加上与x异或为1的count的数
result += p->next[x^1]->count;
p = p->next[x];//依然走向x分支
}
if(p == NULL)
break;
}
return result;
}
int main()
{
int n,m,i;
cin>>n>>m;
Trie * root = new Trie();
for(i =0;i < n;i++)
{
cin>>a[i];
insert(root, a[i]);
}
long long result=0;
for(i =0;i < n;i++)
result += find(root, m, a[i]);
result = result/2;
cout<<result<<endl;
return 0;
}
复习一下Trie树
Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树。
Trie树可以利用字符串的公共前缀来节约存储空间。如下图所示,该trie树用10个节点保存了6个字符串tea,ten,to,in,inn,int:
在该trie树中,字符串in,inn和int的公共前缀是“in”,因此可以只存储一份“in”以节省空间。当然,如果系统中存在大量字符串且这些字符串基本没有公共前缀,则相应的trie树将非常消耗内存,这也是trie树的一个缺点。
Trie树的基本性质可以归纳为:
(1)根节点不包含字符,除根节点意外每个节点只包含一个字符。
(2)从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
(3)每个节点的所有子节点包含的字符串不相同。
字母树的插入(Insert)、删除( Delete)和查找(Find)都非常简单,用一个一重循环即可,即第i 次循环找到前i 个字母所对应的子树,然后进行相应的操作。实现这棵字母树,我们用最常见的数组保存(静态开辟内存)即可,当然也可以开动态的指针类型(动态开辟内存)。下面给出动态开辟内存的实现:
#define MAX_NUM 26
enum NODE_TYPE{ //"COMPLETED" means a string is generated so far.
COMPLETED,
UNCOMPLETED
};
struct Node {
enum NODE_TYPE type;
char ch;
struct Node* child[MAX_NUM]; //26-tree->a, b ,c, .....z
};
struct Node* ROOT; //tree root
struct Node* createNewNode(char ch){
// create a new node
struct Node *new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->ch = ch;
new_node->type == UNCOMPLETED;
int i;
for(i = 0; i < MAX_NUM; i++)
new_node->child[i] = NULL;
return new_node;
}
void initialization() {
//intiazation: creat an empty tree, with only a ROOT
ROOT = createNewNode(' ');
}
int charToindex(char ch) { //a "char" maps to an index<br>
return ch - 'a';
}
int find(const char chars[], int len) {
struct Node* ptr = ROOT;
int i = 0;
while(i < len) {
if(ptr->child[charToindex(chars[i])] == NULL) {
break;
}
ptr = ptr->child[charToindex(chars[i])];
i++;
}
return (i == len) && (ptr->type == COMPLETED);
}
void insert(const char chars[], int len) {
struct Node* ptr = ROOT;
int i;
for(i = 0; i < len; i++) {
if(ptr->child[charToindex(chars[i])] == NULL) {
ptr->child[charToindex(chars[i])] = createNewNode(chars[i]);
}
ptr = ptr->child[charToindex(chars[i])];
}
ptr->type = COMPLETED;
}