Magic题解

Magic题解

题意简述:

给定\(n\)个数\(a_1,a_2,…,a_n\),设对于数\(x\)\(|x|\)表示其在十进制下的位数,也即\(10^{|x|}\le x<10^{|x|+1}\)

需要计算:

\[\sum_{i=1}^n\sum_{j=i+1}^na_i\oplus a_j \]

数据范围:

\(n\le 5\times 10^4,a_i\le 10^{18}\)

分析

这个数据范围启发我们设计一个带\(\log\)的算法

如果我们设

\(S_k=\)\(\sum_{i=1}^n\sum_{j=i+1}^n[a_i\oplus a_j\ge k]\)

则我们要求解的答案是:

\[\sum_{k=1}^{18}k(S_{10^k}-S_{10^{k+1}})+\frac{n(n-1)}{2}=S_0+S_{10}+…+S_{10^{18}} \]

所以问题转化为求解\(S\)

关于异或的问题,不难想到使用字典树进行求解,设我们已经将所有的\(a\)插入到字典树中。

数据范围是允许我们将求解每一个\(S\)的复杂度为\(O(n\log n)\).这正好就是在字典树中遍历\(n\)个数的复杂度。

这启发我们设计函数solve(s,t)表示对于\(s\)计算其对\(S_t\)的贡献。

\(s,t\)的二进制表示分别为\((b_k,b_{k-1}…b_1),(c_k,c_{k-1}…c_1)\)

对于答案的计算,可以将其分为两部分:可以在当前位解决的和留在后续解决的。

则我们设指针\(p\)\(k\)遍历到\(1\),按位统计贡献,将这个过程看作我们不断填数的过程,设所填数为\(x\),其二进制表示为\((d_k,d_{k-1}…d_1)\)

分类讨论:

  1. \(b_p=1,c_p=1\)。此时,我们若想要保持\(x\oplus s>t\)的可能,就需要让\(x\oplus s\)的第\(p\)位为1,也即我们填上\(d_p=0\),此时我们无法计算出答案
  2. \(b_p=0,c_p=1\),类比情况一,填上\(d_p=1\)
  3. \(b_p=1,c_p=0\),此时我们将\(d_p\)填为\(1/0\),当填为\(0\)时,第\(p-1\sim 1\)位无论填什么都可以对答案产生贡献,所以直接累加上\(siz[t[p][0]]\)的贡献并将\(d_p\)填为\(1\)即可。
  4. \(b_p=0,c_p=0\),类比情况三,可以得到算上\(siz[t[p][1]]\)的贡献然后将\(d_p\)填为0即可。

核心代码:

int solve(int s,int tt){//在trie中查找与s异或比t大的数的数量 
	int p=1,ans=0;
	for(int i=60;i>=0;i--){
		if(!p)return ans;
		int x=(s>>i)&1;
		if((tt>>i)&1){
			p=t[p][x^1];
		}
		else{
			ans+=siz[t[p][x^1]];
			p=t[p][x];
		}
	}
	return ans;
}
posted @ 2023-01-11 08:06  spdarkle  阅读(18)  评论(0编辑  收藏  举报