字典树andXOR* A.HDU 4825 B. HDU 5536 C. BZOJ 4260 D. POJ 3764 E. HDU 4757 F. HDU 5661 G. BZOJ 4245 I. J. 字典树在字符串上的应用,略 K. HDU 4099

字典树andXOR*
A.HDU 4825
B. HDU 5536
C. BZOJ 4260
D. POJ 3764
E. HDU 4757
F. HDU 5661
G. BZOJ 4245
I. J. 字典树在字符串上的应用,略
K. HDU 4099

你需要写一种数据结构:

  • 往其中加入一个正整数;
  • 找出一个正整数,使得该数与询问数的异或结果最大。

(nle 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:
",TT);
		while(m--){
			long long x;
			scanf("%lld",&x);
			printf("%lld
",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)
( ext{Time Limit=9s})

直接 (O(n^2log 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]))(1le l_1le r_1<l_2le r_2le n))
(nle 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_{jle 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_{iin [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_{ale xle b,cle yle d}(x oplus y)(a,b,c,dle 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
",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),然后全部插到字典树里。