[CSP-S模拟测试]:big(Trie树+贪心)

本文探讨了一个涉及博弈论和Trie树算法的复杂问题,目标是在一系列操作后找到整数x的最大可能值及其数量。通过对x进行特定的逻辑左移操作并利用Trie树进行高效搜索,文章详细阐述了如何通过构建Trie树和深度优先搜索(DFS)来解决这一挑战。

题目描述

你需要在$[0,2^n)$中选一个整数$x$,接着把$x$依次异或$m$个整数$a_1~a_m$。
在你选出$x$后,你的对手需要选择恰好一个时刻(刚选完数时、异或一些数后或是最后),将$x$变为$(\left \lfloor{\frac{2\times x}{2^n}}\right \rfloor \mod 2^n)$你想使$x$最后尽量大,而你的对手会使$x$最后尽量小。
你需要求出$x$最后的最大值,以及得到最大值的初值数量。


输入格式

第一行两个整数$n,m$。
第二行$m$个整数$a_1~a_m$。


输出格式

第一行输出一个整数,表示$x$最后的最大值。
第二行输出一个整数,表示得到最大值的初值数量。
第一个数正确得$6$分,两个数都正确再得$4$分。


样例

样例输入

2 3
1 2 3

样例输出

1
2


数据范围与提示

样例解释:

$x=0$时得到$0$,$x=1$时得到$1$,$x=2$时得到$1$,$x=3$时得到$0$。

数据范围:

对于$20\%$的数据,$n\leqslant 10$,$m\leqslant 100$。
对于$40\%$的数据,$n\leqslant 10$,$m\leqslant 1,000$。
对于另外$20\%$的数据,$n\leqslant 30$,$m\leqslant 10$。
对于$100\%$的数据,$n\leqslant 30$,$m\leqslant 100,000$,$0\leqslant a_i<2^n$。


题解

注意上面那个神奇的式子:$(\left \lfloor{\frac{2\times x}{2^n}}\right \rfloor \mod 2^n)$。

刚一看时一脸懵,定睛一看……还是一脸懵……

其实没有那么难,这道题就是将$x$进行了逻辑左移,其实就是说将$x$左移了一位,并将最高为补到了最后面。

那么我们来退一下式子:

  先设$q[i]$表示$a$数组的异或前缀和,对手在$i$时刻使出“技能”。

  那么:$ans=((x\ xor\ a_i)\ll 1)xor(a_i\ xor\ a_m)$。

  展开:$ans=(x\ll 1)xor(a_i\ll 1)xor\ a_i\ xor\ a_m$。

  那么我们需要找的是一个$x$使其$xor(a_i\ll 1)xor\ a_i\ xor\ a_m$最大。

一般碰到$01$串的题就考虑$Trie$树,这道题一样,我们来构建一个$01Trie$数,将所有$a_i$逻辑左移的值$xor$起来,注意对手可以在一开始就使用“技能”,所以一开始要插入,依次$xor$其后缀和插入$Trie$树,然后在这棵$Trie$树上跑$DFS$即可。

在$DFS$时分为一下三种情况:

  $\alpha.$当前节点既有$0$孩子,也有$1$孩子时,需要往两侧走,$ans$不变。

  $\beta.$如果有$0$儿子,没有$1$儿子,那么往$0$儿子走一定更优。

  $\gamma.$如果有$1$儿子,没有$0$儿子,同理,不再赘述。

统计答案排个序就好了。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[200000],sum[200000];
int trie[4000000][2],cnt=1;
int ans[4000000],ansss;
int flag;
void insert(int x)//插入
{
    int num=1;
    for(int i=1;i<=n;i++)
    {
        if(!trie[num][(x>>(n-i)&1)])trie[num][(x>>(n-i)&1)]=++cnt;
        num=trie[num][(x>>(n-i)&1)];
    }
}
void dfs(int x,int num,int dep)//dfs
{
	if(dep<0)
	{
		ans[++ans[0]]=num;
		return;
	}
	if(!trie[x][0]&&trie[x][1])dfs(trie[x][1],num^(1<<dep),dep-1);//三种情况
	else if(trie[x][0]&&!trie[x][1])dfs(trie[x][0],num^(1<<dep),dep-1);
	else{dfs(trie[x][0],num,dep-1);dfs(trie[x][1],num,dep-1);}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d",&a[i]);
	for(int i=m;i;i--)sum[i]=sum[i+1]^a[i];
	for(int i=0;i<=m;i++)
	{
		flag=flag^(2*a[i]/(1<<n)+2*a[i])%(1<<n);//逻辑左移
		insert(flag^sum[i+1]);//插入
	}
	dfs(1,0,n-1);
	sort(ans+1,ans+ans[0]+1);
	for(int i=ans[0];i;i--)
		if(ans[i]==ans[ans[0]])ansss++;
		else break;
	printf("%d\n%d",ans[ans[0]],ansss);
	return 0;
}

rp++

转载于:https://www.cnblogs.com/wzc521/p/11288658.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值