1. 程式人生 > >01字典樹加強版HDU 5536

01字典樹加強版HDU 5536

01字典樹加強版HDU 5536

http://acm.hdu.edu.cn/showproblem.php?pid=5536

  1. 簡化版題意:
    在一個數組中找出 (s[i]+s[j])^s[k] 最大的值,其中 i、j、k 各不相同。

  2. 這個題目我們就不能直接套用模板了,問題出在哪裡?是因為i、j、k不能相同,所以直接查詢tire樹會出現問題。怎麼辦?一個很直觀的想法是刪除。對k建trie樹,列舉i、j,然後將i、j在trie樹中刪除。

  3. 考慮如何刪除,我們用一個num陣列記錄每一個結點出現的次數(注意是每一個結點而不是結尾的結點,因為如果i,j兩個位置的數字不能使用,那麼在tire樹上所有關於他的結點都不能使用),在每次insert的時候增加num,在每次刪除的時候減少num,在最後查詢的時候判斷num是否為0代表當前樹上這個結點是否可以使用。說到這裡,這個題的大致思路就說完了,下面直接上程式碼。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define mk(a,b) make_pair(a,b)
#define ll long long
#define N 1010	
using namespace std;
ll tot,ch[N*32][3],val[N*32],num[N*32],t,a[N];
void init()
{
	tot=1;
	ch[0][1]=0;ch[0][0]=0;
}
void insert(ll x)
{
	ll u=0;
	for(ll i=32;i>=0;i--)
	{
		ll v=(x>>i)&1;
		if(!ch[u][v])
		{
			ch[tot][0]=0;ch[tot][1]=0;
			val[tot]=0;
			num[tot]=0;
			ch[u][v]=tot++;
		}
		u=ch[u][v];
		num[u]++;
	}
	val[u]=x;
}
void del(ll x,ll add)
{
	ll u=0;
	for(ll i=32;i>=0;i--)
	{
		ll v=(x>>i)&1;
		u=ch[u][v];
		num[u]+=add;
	}
}
ll query(ll x)
{
	ll u=0;
	for(ll i=32;i>=0;i--)
	{
		ll v=(x>>i)&1;
		if(ch[u][v^1]&&num[ch[u][v^1]]) u=ch[u][v^1];
		else  u=ch[u][v];
	}
	return x^val[u];
}
int main()
{
	scanf("%lld",&t);
	for(ll k=1;k<=t;k++)
	{
		ll n,ans=0;
		init();
		scanf("%lld",&n);
		for(ll i=1;i<=n;i++)
		{
		  scanf("%lld",&a[i]);
		  insert(a[i]);
		}
		for(ll i=1;i<=n;i++)
		  for(ll j=i+1;j<=n;j++)
		  {
		  	  del(a[i],-1);del(a[j],-1);
		  	  ans=max(ans,query(a[i]+a[j]));
		  	  insert(a[i]);insert(a[j]);
		  }
		printf("%lld\n",ans);
	}
	return 0;
}