异或与字典树的应用

异或与字典树的应用


0.异或的相关性质.

1. a a = 0 , 0 a = a 0 = a , a N 1.a\oplus a=0,0\oplus a=a\oplus0=a,a\in N

2. a b = b a 2.a\oplus b=b\oplus a

3. a b = c a c = b b c = a 3.a\oplus b=c\Leftrightarrow a\oplus c=b\Leftrightarrow b\oplus c=a

4. 4. a 1 a 2 a 3 a i = p r e [ i ] a_1\oplus a_2\oplus a_3\dots\oplus a_i=pre[i]

a l a l + 1 a r 1 a r = p r e [ l 1 ] p r e [ r ] a_l\oplus a_{l+1}\dots\oplus a_{r-1}\oplus a_r=pre[l-1]\oplus pre[r]

5. ( a b ) c = a ( b c ) 5.(a\oplus b)\oplus c=a\oplus(b\oplus c)


1.异或在字典树的优化.

考虑下列问题:

一.给定序列 a 1 , a 2 , a n a_1,a_2\dots,a_n ,求 m a x { a i a j } , ( 1 i < j n ) max\{a_i\oplus a_j\},(1\le i<j\le n)
n 1 e 5 n\leq 1e5

1.暴力枚举 i , j i,j 位置,复杂度为 O ( n 2 ) O(n^2) 显然不行。

2.考虑对内层循环优化.

对于前 i 1 i-1 个数建立一棵从高位到低位的 01 01 字典树,然后贪心地从最高位开始选,根据异或的(不同为1,相同为0)的原则,显然优先考虑不同的位,若该位没有结点,则只能选择当前已有的结点,一直走到叶子结点结束的值就是最大值。

扫描二维码关注公众号,回复: 11487977 查看本文章

每次插入字符和查询都是 O ( 31 ) = l o g ( i n t ) O(31)=log(int)

总时间复杂度: O ( n 2 ) O ( 31 n ) O(n^2)\rightarrow O(31n)

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+5,M=3e6+5;
int n,a[N],son[M][2],cnt;
void insert(int x){
    int p=0;
    for(int i=30;~i;i--){
        int &s=son[p][x>>i&1];
        if(!s) s=++cnt;
        p=s;
    }
}
int query(int x){
    int ans=0,p=0;
    for(int i=30;~i;i--){
        int s=x>>i&1;
        if(son[p][!s]) ans+=1<<i,p=son[p][!s];
        else p=son[p][s];
    }
    return ans;
}
int main(){
    cin>>n;
    int ans=0;
    for(int i=0;i<n;i++)
        cin>>a[i],ans=max(ans,query(a[i])),insert(a[i]);
    cout<<ans<<endl;
}

二.给定序列 a 1 , a 2 , a n a_1,a_2\dots,a_n ,求最大区间异或和.

显然预处理前缀和,然后方法同上。


三.给定序列 a 1 , a 2 , a n a_1,a_2\dots,a_n ,求两段不相交区间最大异或和.

显然预处理前缀和与后缀和,然后取 m a x { f p r e [ i ] + f s u f [ i + 1 } , ( i [ 1 , n ) ) max\{f_{pre}[i]+f_{suf}[i+1\},(i\in[1,n))

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e5+5,M=8e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int a[N],son[N][2],n,pre[N],suf[N],f[N],cnt=0;
void insert(int x){
	int p=0;
	for(int i=30;~i;i--){
		int &s=son[p][x>>i&1];
		if(!s) s=++cnt;
		p=s;
	}
}
int query(int x){
	int p=0,ans=0;
	for(int i=30;~i;i--){
		int s=x>>i&1;
		if(son[p][!s]) ans+=1<<i,p=son[p][!s];
		else p=son[p][s];
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++ )scanf("%d",&a[i]),pre[i]=pre[i-1]^a[i];
	for(int i=n;i;i--) suf[i]=suf[i+1]^a[i];
	for(int i=1;i<=n;i++){
		f[i]=max(f[i-1],query(pre[i]));
		insert(pre[i]); 
	}
	int ans=0;mst(son),cnt=0;
	for(int i=n;i;i--){
		ans=max(ans,f[i-1]+query(suf[i]));
		insert(suf[i]);
	}
	printf("%d\n",ans);
	return 0;
}

四.给定一棵树,求树上路径的最大异或和.

考虑以1为根进行 d f s dfs ,求出所有节点 i i 1 1 的路径异或和 f [ i ] f[i] ,然后根据树的性质可知:

X o r u v = f [ u ] f [ v ] Xor_{u-v}=f[u]\oplus f[v]

然后就可以用 01 01 字典树的板子了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=3e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int n,son[M][2],f[N],h[N],cnt,tot;
struct node{
	int to,nt,w;
}e[N<<1];
void add(int u,int v,int w){
	e[++tot]={v,h[u],w},h[u]=tot;
	e[++tot]={u,h[v],w},h[v]=tot;
}
void dfs(int u,int fa){
	for(int i=h[u];i;i=e[i].nt){
		 if(e[i].to==fa) continue;
		 int v=e[i].to;
		 f[v]=f[u]^e[i].w;
		 dfs(v,u);
	}
}
void ins(int x){
	int p=0;
	for(int i=30;~i;i--){
		int &s=son[p][x>>i&1];
		if(!s) s=++cnt;
		p=s;
	}
}
int que(int x){
	int p=0,ans=0;
	for(int i=30;~i;i--){
		int s=x>>i&1;
		if(son[p][!s]) ans+=1<<i,p=son[p][!s];
		else p=son[p][s];
	} 
	return ans; 
}
int main(){
	while(~scanf("%d",&n)){
	for(int i=0;i<=n;i++) h[i]=f[i]=0;tot=cnt=0;
	mst(son);
	for(int i=1,u,v,w;i<n;i++){
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
	}
	dfs(0,-1);int ans=0;
	for(int i=0;i<=n;i++){
		ans=max(ans,que(f[i]));
		ins(f[i]);
	}
	printf("%d\n",ans);
	}
	return 0;
	
}

五.给定数组,求 m a x { ( a [ i ] + a [ j ] ) a [ k ] } , i , j , k max\{(a[i]+a[j])\oplus a[k]\},i,j,k 互不相同。
见下面的传送门讲解。
传送门

猜你喜欢

转载自blog.csdn.net/weixin_45750972/article/details/107586527