Magic题解

Magic题解

题意简述:

给定n个数a1,a2,,an,设对于数x|x|表示其在十进制下的位数,也即10|x|x<10|x|+1

需要计算:

i=1nj=i+1naiaj

数据范围:

n5×104,ai1018

分析

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

如果我们设

Sk=i=1nj=i+1n[aiajk]

则我们要求解的答案是:

k=118k(S10kS10k+1)+n(n1)2=S0+S10++S1018

所以问题转化为求解S

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

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

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

s,t的二进制表示分别为(bk,bk1b1),(ck,ck1c1)

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

则我们设指针pk遍历到1,按位统计贡献,将这个过程看作我们不断填数的过程,设所填数为x,其二进制表示为(dk,dk1d1)

分类讨论:

  1. bp=1,cp=1。此时,我们若想要保持xs>t的可能,就需要让xs的第p位为1,也即我们填上dp=0,此时我们无法计算出答案
  2. bp=0,cp=1,类比情况一,填上dp=1
  3. bp=1,cp=0,此时我们将dp填为1/0,当填为0时,第p11位无论填什么都可以对答案产生贡献,所以直接累加上siz[t[p][0]]的贡献并将dp填为1即可。
  4. bp=0,cp=0,类比情况三,可以得到算上siz[t[p][1]]的贡献然后将dp填为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 @   spdarkle  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示