字典树andXOR*
A.HDU 4825
你需要写一种数据结构:
- 往其中加入一个正整数;
- 找出一个正整数,使得该数与询问数的异或结果最大。
\(n\le 10^5\) 。
解
将数按二进制从高位到低位插入字典树。
查询时从高位往低位贪心。
Code
#include<cstdio>
using namespace std;
const int maxn=100003,maxlog=33;
int t[maxn*maxlog][2],cnt;
void insert(long long x){
int p=1;
for(int i=32;i>=0;i--){
int nxt=(x>>i)&1;
if(!t[p][nxt])t[p][nxt]=++cnt;
p=t[p][nxt];
}
}
long long query(long long x){
int p=1;
long long ret=0;
for(int i=32;i>=0;i--){
int nxt=!((x>>i)&1);
if(t[p][nxt])p=t[p][nxt],ret|=(long long)nxt<<i;
else p=t[p][!nxt],ret|=(long long)(!nxt)<<i;
}
return ret;
}
int n,m;
int main(){
int T;
scanf("%d",&T);
for(int TT=1;TT<=T;TT++){
scanf("%d%d",&n,&m);
cnt=1;
for(int i=0;i<=n*maxlog;i++)t[i][0]=t[i][1]=0;
for(int i=1;i<=n;i++){
long long x;
scanf("%lld",&x);
insert(x);
}
printf("Case #%d:\n",TT);
while(m--){
long long x;
scanf("%lld",&x);
printf("%lld\n",query(x));
}
}
return 0;
}
B. HDU 5536
有一个序列 \(s\) ,求 \(\max_{i,j,k,i≠j≠k} (s_i+s_j) \oplus s_k\) 。
\(1≤T≤1000\)
\(3≤n≤1000\)
\(0≤si≤10^9\)
There are at most \(10\) testcases with \(n>100\)
\(\text{Time Limit=9s}\)
解
直接 \(O(n^2\log n)\) 暴力插字典树。
C. BZOJ 4260
有一个序列 \(A\) ,求 \(\max ((A[l_1] \oplus A[l_1+1] \oplus \dots \oplus A[r_1])+(A[l_2] \oplus A[l_2+1] \oplus \dots \oplus A[r_2]))(1\le l_1\le r_1<l_2\le r_2\le n)\)
\(n\le 4*10^5\) 。
解
处理出:
\(pre[i]=a[1] \oplus a[2] \oplus \dots \oplus a[i]\)
\(suf[i]=a[i] \oplus a[i+1] \oplus \dots \oplus a[n]\)
\(B[i]=\max(\max_{j\le i}pre[i] \oplus pre[j-1],B[i-1])\)
\(C[i]=\max(\max_{j≥i}suf[i] \oplus suf[j+1],C[i+1])\)
答案 \(=\max_{i\in [1,n-1]}(B[i]+C[i+1])\)
D. POJ 3764
有一棵树,有边权,求异或和最大的路径的异或和。
解
设 \(dp[i]\) 为从1到 \(i\) 的路径的异或和,答案 \(=\max_{i,j}(dp[i] \oplus dp[j])\) 。
请自己证明。
E. HDU 4757
一棵树, \(Q\) 次询问节点 \(x\) 到节点 \(y\) 的路径上点权与 \(x\) 的异或的最大值。
解
可持久化trie
F. HDU 5661
求 \(\max_{a\le x\le b,c\le y\le d}(x \oplus y)(a,b,c,d\le 10^{18})\) 。
解
从高位往低位贪心。
Code
#include<cstdio>
using namespace std;
const int maxlog=60;
bool valid(long long ans,int i,long long bit,long long l,long long r){
bit=ans|(bit<<i);
return (bit|((1ll<<i)-1))>=l&&bit<=r;
}
int main(){
int T;
scanf("%d",&T);
for(int TT=1;TT<=T;TT++){
long long a,b,c,d,ans1=0,ans2=0;
scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
for(int i=59;i>=0;i--){
bool v10=valid(ans1,i,0,a,b),v11=valid(ans1,i,1,a,b),v20=valid(ans2,i,0,c,d),v21=valid(ans2,i,1,c,d);
if(v10&&v21)ans2|=1ll<<i;
else if(v11&&v20)ans1|=1ll<<i;
else if(v11&&v21)ans1|=1ll<<i,ans2|=1ll<<i;
}
printf("%lld\n",ans1^ans2);
}
return 0;
}
G. BZOJ 4245
给定一个长度为 \(n\) 的序列 \(a[1],a[2],\dots ,a[n]\),请将它划分为 \(m\) 段连续的区间,设第 \(i\) 段的费用 \(c[i]\) 为该段内所有数字的异或和,则总费用为 \(c[1] | c[2] | \dots | c[m]\) 。请求出总费用的最小值。
解
记录一下前缀异或和,然后枚举一个高位,如果存在一个高位使得至少 \(m\) 个异或和为 \(0\) 且总异或和 \((prev[n])\) 为 \(0\) ,那么即可划分,此时这一位对答案的贡献为 \(0\) ,否则该位对答案的贡献为一个 \(2\) 的幂。
I. J. 字典树在字符串上的应用,略
K. HDU 4099
\(T=50000\) 组询问,每次给出一个长度不超过40的数,求最小的斐波那契数,使得给定的数是这个数的前缀。如果前100000个数中没有符合条件的,输出-1。
解
高精计算前100000个斐波那契数,只需存储前50位(类似double),然后全部插到字典树里。