选数 题解

选数 题解

首先,设最初取值为x,按照套路,我们设异或前缀和:prei=a1a2ai,设f(x)=(2x2n+2x)mod2n

注意到:0ai<2n,也即其二进制下不会超过n位。

那么我们实际上要求的即为:maxi=0mf(xprei)prenprei

简单带入一下f,容易得到:

f(xprei)=f(x)f(prei)

有趣的是,f(x)相当于是将x在二进制下向左做一个一个单位长度的循环

这也可以证明我们对下列所有的f的变换是正确的,每一个f(x)都有唯一的值与之对应。

所以式子变为:f(x)f(prei)prenprei,其中后半截的可以预处理的,不妨设si=f(prei)prenprei

s1sn插入trie,并且注意到可以在不异或任何值的情况下对x进行变化,所以需要插入0别问90pts是为什么

再者,再看这个式子:maxi=0mf(x)si,注意到x[0,2n1],f(x)[0,2n1],所以可以将f(x)x替换掉。

这样的话,式子化为maxi=0mxsi,我们的问题就变成了如何求得这个最大值。

我们设solve(p,now,ans)表示当前遍历到trie的指针p,已经计算到第now位,答案最大为ans

此时我们来分类讨论,毕竟先后手都足够聪明。

  1. p有两个儿子,则不论第now位填什么,都有决策使这个位变为0,所以递归solve(t[p][0],now-1,ans<<1),slove(t[p][1],now-1,ans<<1)
  2. p仅有一个儿子,则可以反着填数,使这一位变成1。设儿子为k,则递归solve(k,now-1,ans<<1|1)
  3. p没有儿子,直接返回(ans,1)(最大值和个数)

最后返回值是返回各个分支的最大值,注意个数的统计。

核心代码如下:

#define pr pair<int,int>
#define mk make_pair
pr query(int p,int ans){
	if(t[p][0]==0&&t[p][1]==0)return mk(ans,1);
	if(t[p][0]&&t[p][1]){
		pr ans1=query(t[p][0],ans<<1);
		pr ans2=query(t[p][1],ans<<1);
		if(ans1.first==ans2.first)return mk(ans1.first,ans1.second+ans2.second);
		return max(ans1,ans2);
	}
	if(t[p][0])return query(t[p][0],ans<<1|1);
	return query(t[p][1],ans<<1|1);
}
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	n2=1;for(int i=1;i<=n;i++)n2*=2;
	for(int i=1;i<=m;i++)cin>>a[i];
	for(int i=1;i<=m;i++)pre[i]=pre[i-1]^a[i];
	for(int i=1;i<=m;i++)s[i]=get(pre[i])^pre[n]^pre[i];
	for(int i=1;i<=m;i++)insert(s[i]);
	insert(0);
	pr ans=query(1,0);
	cout<<ans.first<<endl<<ans.second<<endl;
}
posted @   spdarkle  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示