HDU - 5536 字典树

题意:

给出长度为n的序列s1到sn,以及一个表达式 (si + sj) ^ sk的最大值,^代表异或运算,其中i,j,k不能相同。

思路:

一个典型的字典树处理异或和的问题,思路就是先将所有si按照二进制位从高到低拆成32位然后储存在字典树中,然后枚举i和j,让s[i]+s[j]在字典树上爬,每一位尽量往与当前位数相反的方向爬,这样最终结果这一位就是1,如果相反方向节点不存在,那么往相同方向爬,最终结果这一位就是0。另外为了保证下标不重复,每次枚举了i,j之后要先将i,j从字典树中删除,然后再计算结果更新最大值。

代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1005;
const int maxbit = 35;

ll s[MAXN];

struct node {
	node* Next[2];
	ll cnt;
};

struct Trie {
	node *root;

	void init() {			// 字典树初始化
		root = new node;
		for (int i = 0; i < 2; i++) 
			root->Next[i] = NULL;
	}

	void ins(ll x) {		// 插入节点
		node *p = root;
		for (int i = maxbit; i >= 0; i--) {
			int id = (x & (1LL << i)) ? 1 : 0;
			if (p->Next[id] == NULL) {
				node *q = new node;
				for (int j = 0; j < 2; j++) q->Next[j] = NULL;
				p->Next[id] = q;
				p = p->Next[id];
				p->cnt = 1; 
			} 
			else {
				p = p->Next[id];
				p->cnt++;
			}
		}
	}

	void del(ll x) {		// 删除节点
		node *p = root;
		for (int i = maxbit; i >= 0; i--) {
			int id = (x & (1LL << i)) ? 1 : 0;
			p = p->Next[id];
			p->cnt--;
		}
	}

	ll cal(ll x) {			// 在字典树上爬,计算最大异或和
		node *p = root;
		ll res = 0;
		for (int i = maxbit; i >= 0; i--) {
			int id = (x & (1LL << i)) ? 1 : 0;
			res <<= 1;
			if (p->Next[id ^ 1] != NULL && p->Next[id ^ 1]->cnt > 0) {
				p = p->Next[id ^ 1];
				res += 1;
			}
			else p = p->Next[id];
		}
		return res;
	}

	void cle(node *p) {		// 清空字典树,回收空间
		if (p == NULL) return;
		if (p->Next[0] != NULL) cle(p->Next[0]);
		if (p->Next[1] != NULL) cle(p->Next[1]);
		free(p);
		p = NULL;
 	}
} trie;

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		int n;
		scanf("%d", &n);
		trie.init();
		for (int i = 1; i <= n; i++) {
			scanf("%lld", &s[i]);
			trie.ins(s[i]);
		}
		ll ans = 0;
		for (int i = 1; i <= n; i++) {
			trie.del(s[i]);
			for (int j = i + 1; j <= n; j++) {
				trie.del(s[j]);
				ans = max(ans, trie.cal(s[i] + s[j]));
				trie.ins(s[j]);
			}
			trie.ins(s[i]);
		}
		trie.cle(trie.root);
		printf("%lld\n", ans);
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值