$$ \newcommand{\seq}[2]{{#1}_{1},{#1}_{2},\cdots,{#1}_{#2}} \newcommand{\num}[1]{1,2,\cdots,#1} \newcommand{\stra}[2]{\begin{bmatrix}#1 \\ #2\end{bmatrix}} \newcommand{\strb}[2]{\begin{Bmatrix}#1 \\ #2\end{Bmatrix}} \newcommand{\dw}[1]{\underline{#1}} \newcommand{\up}[1]{\overline{#1}} $$

字典树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),然后全部插到字典树里。

posted @ 2019-05-18 14:23  chc_1234567890  阅读(111)  评论(0编辑  收藏  举报