题目

第一道题:传送门 to CF。下面就以这道题为例。

第二道题:传送门 to CF。

思路

解决计数问题,先想判定问题。在这道题里,我已经因此吃了大亏了……

如果是判定问题,可以考虑求解 max⁡(ai⊕aj)\max(a_i\oplus a_j)max(ai​⊕aj​) 。最常见的方法是,依次插入每个数的同时判断 max⁡\maxmax 。但显然在这里就不易于扩展了;我们最好找一个不基于动态插入的做法。

其实就是暴力。模拟一次就知道了。从根开始,如果 0,10,10,1 都非空,就得在这两个子树内各选出一个;否则就在某个儿子子树内选出俩。所以可以记 ⟨x,y⟩\langle x,y\rangle⟨x,y⟩ 表示 xxx 和 yyy 子树内各选出一个是目前的 max⁡\maxmax 之一(前缀最优)。一层一层转移,如果这一层的所有 ⟨x,y⟩\langle x,y\rangle⟨x,y⟩ 都不能造出 111,那就转移到 ⟨x0,y0⟩,⟨x1,y1⟩\langle x_0,y_0\rangle,\langle x_1,y_1\rangle⟨x0​,y0​⟩,⟨x1​,y1​⟩,其中 xdx_dxd​ 表示 xxx 的 ddd 儿子。否则就转移到 ⟨x0,y1⟩,⟨x1,y0⟩\langle x_0,y_1\rangle,\langle x_1,y_0\rangle⟨x0​,y1​⟩,⟨x1​,y0​⟩ 。

有趣的是,复杂度是 O(nlog⁡V)\mathcal O(n\log V)O(nlogV) 的,即 trie\tt trietrie 的点数。因为每个点最多出现在一个二元组中。

计数问题又怎么做呢?其实很简单,必须 把判定问题时所需的状态记录下来,就像这道题,根本不用怕。因为内层已经几乎是 dp\tt dpdp 了,它只保留了最精简的信息,一般不会再简化了。

所以我们要记录一大堆 ⟨x,y⟩\langle x,y\rangle⟨x,y⟩ 吗?其实并不用。因为上面的 “一层一层转移” 并非必要。分析复杂度发现,不管这一位是选 000 还是选 111,每个点还是只能出现在一个二元组中,复杂度仍然正确。所以我们不必全局考虑,只需暴力递归。但是二元组肯定是基本信息,不能再减。

于是记 f(x,y)f(x,y)f(x,y) 为,最大值是 x,yx,yx,y 子树内的两个数异或得到的,有多少种从二者子树中选数的方案。如果 max⁡\maxmax 的这一位是 000,那么要么只有 x0,y0x_0,y_0x0​,y0​ 存在,要么只有 x1,y1x_1,y_1x1​,y1​ 存在,分别递归,然后加起来。

如果 max⁡\maxmax 的这一位是 111 呢?就有三种情况了:

  • 有 x0,y1x_0,y_1x0​,y1​,而 x1,y0x_1,y_0x1​,y0​ 至少有一个是空的。递归 f(x0,y1)f(x_0,y_1)f(x0​,y1​),乘系数 2size(x1)+2size(y0)−12^{size(x_1)}+2^{size(y_0)}-12size(x1​)+2size(y0​)−1 即可。此处 −1-1−1 是因为 ∅\varnothing∅ 被算了两次。
  • 有 x1,y0x_1,y_0x1​,y0​,而 x0,y1x_0,y_1x0​,y1​ 至少有一个是空的。同上。
  • 同时有 x0,y1x_0,y_1x0​,y1​ 和 x1,y0x_1,y_0x1​,y0​ 。此时两边都要递归;怎么合并 max⁡\maxmax 呢?

不要陷入求 max⁡\maxmax 的误区!判定问题并不是求 max⁡\maxmax,而是 max⁡⩽x\max\leqslant xmax⩽x 。很容易看出,两边其实都 ⩽x\leqslant x⩽x 就可以了!所以直接乘法原理相乘即可。

但是我们的计数问题好像会考虑 max⁡\maxmax 这一位是 000 和 111 两种情况,都递归,复杂度就不正确了!所以已经 ⩽x\leqslant x⩽x 的时候不能递归,要用 sizesizesize 直接计算。

时间复杂度 O(nlog⁡V)\mathcal O(n\log V)O(nlogV) 。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){int a = 0, c = getchar(), f = 1;for(; !isdigit(c); c=getchar())if(c == '-') f = -f;for(; isdigit(c); c=getchar())a = (a<<3)+(a<<1)+(c^48);return a*f;
}const int MOD = 998244353;
inline void modAddUp(int &x,const int y){if((x += y) >= MOD) x -= MOD;
}const int MAXN = 150005, LOGX = 30;
int powtwo[MAXN];
namespace Trie{int ch[MAXN*LOGX][2], cntNode = 1;int siz[MAXN*LOGX];void insert(int x){int o = 1; // avoid 0for(int i=LOGX-1; ~i; --i){int &d = ch[o][x>>i&1];if(!d) d = ++ cntNode;o = d, ++ siz[o];}}# define _dfs(a,b) dfs(ch[x][a],ch[y][b],bound,d-1)int dfs(int x,int y,const int bound,int d=LOGX-1){int res = 0; if(!(~d) || !x || !y) return 0;if(!(bound>>d&1)){ // this must be zerores = _dfs(0,0); modAddUp(res,_dfs(1,1));return res; // no more things to do}// if maximum is 0 on this bitif(x != y) // already forkedrep(v,0,1) res = int((res+(powtwo[siz[ch[y][v]]]-1)*llong(powtwo[siz[ch[x][v]]]-1))%MOD);else rep(v,0,1) modAddUp(res,powtwo[siz[ch[x][v]]]-1);// if maximum is 1 on this bitif(x == y){modAddUp(res,_dfs(0,1));return res; // only way: fork}const int lson = _dfs(0,1), rson = _dfs(1,0);// case 1: generated by <0,1> and <1,0>res = int((res+llong(lson)*rson)%MOD);// case 2: generated by only <0,1>res = int((res+llong(powtwo[siz[ch[x][1]]]+powtwo[siz[ch[y][0]]]-1)*lson)%MOD);// case 3: generated by only <1,0>res = int((res+llong(powtwo[siz[ch[x][0]]]+powtwo[siz[ch[y][1]]]-1)*rson)%MOD);return res;}
}int main(){int n = readint(), bound = readint()+1;rep(i,powtwo[0]=1,n) // pre-computepowtwo[i] = (powtwo[i-1]<<1)%MOD;if(bound>>30){printf("%d\n",powtwo[n]-1);return 0; // everything can be chosen}rep(i,1,n) Trie::insert(readint());printf("%d\n",Trie::dfs(1,1,bound));return 0;
}

拓展

即第二题的做法。考虑 min⁡(ai⊕aj)\min(a_i\oplus a_j)min(ai​⊕aj​) 的判定问题,和 max⁡\maxmax 很像,唯一的区别是,其判断依据为 “子树内是否有至少两个数” 而非 “是否存在该子树” 了。

所以它甚至不需要预处理 2k2^k2k,因为没有这样的选项。

BONUS:若 a⩽b⩽ca\leqslant b\leqslant ca⩽b⩽c 则 min⁡(a⊕b,b⊕c)⩽a⊕c\min(a\oplus b,b\oplus c)\leqslant a\oplus cmin(a⊕b,b⊕c)⩽a⊕c 。证明很简单,考虑 a,ca,ca,c 最高的不相同的 bit\text{bit}bit,由 a⩽b⩽ca\leqslant b\leqslant ca⩽b⩽c 可知 bbb 的前缀与二者都相同;这一位上 bbb 取 000 则 a⊕ba\oplus ba⊕b 这一位上是 000,取 111 则 b⊕cb\oplus cb⊕c 这一位上是 000,但 a⊕ca\oplus ca⊕c 这一位是 111,证毕。

于是可以排序后直接记 f(v)f(v)f(v) 表示以 vvv 结尾的子序列的选法。放在 trie\tt trietrie 上进行判定。复杂度没变;只是更好写。

核心代码

 int dfs_forked(int x,int y,const llong &bound,int d){if(!x || !y || d == -1) return 0;if(bound>>d&1) // have to be 1return (dfs_forked(ch[x][0],ch[y][1],bound,d-1)+dfs_forked(ch[x][1],ch[y][0],bound,d-1))%MOD;// if minimum is 1 on this bitint res = int((llong(siz[ch[x][0]])*siz[ch[y][1]]+llong(siz[ch[x][1]])*siz[ch[y][0]])%MOD);// if minimum is 0 on this bitmodAddUp(res,dfs_forked(ch[x][0],ch[y][0],bound,d-1));modAddUp(res,dfs_forked(ch[x][1],ch[y][1],bound,d-1));return res; // can only choose 1 of each}int dfs(int x,const llong &bound,int d=LOGX-1){if(!x || d == -1) return 0;if(bound>>d&1) // have to be 1 (fork)return dfs_forked(ch[x][0],ch[x][1],bound,d-1);// if minimum is 1 on this bitint res = int(llong(siz[ch[x][0]])*siz[ch[x][1]]%MOD);// if minimum is 0 on this bitconst int lson = dfs(ch[x][0],bound,d-1);const int rson = dfs(ch[x][1],bound,d-1);// case 1: provided by <0,0> and <1,1>res = int((res+llong(lson)*rson)%MOD);// case 2: provided by <0,0>res = int((res+llong(lson)*(siz[ch[x][1]]+1))%MOD);// case 3: provided by <1,1>res = int((res+llong(rson)*(siz[ch[x][0]]+1))%MOD);return res;}

[CF1616H]Keep XOR Low / [CF_GYM102331B]Bitwise Xor相关推荐

  1. CodeForces 1616H Keep XOR Low {a^b≤x} / CodeForces gym102331 Bitwise Xor {a^b≥x}(trie树 + 计数)

    文章目录 CodeForces 1616H Keep XOR Low problem solution code CodeForces gym102331 Bitwise Xor problem so ...

  2. 【CF1616H】Keep XOR Low(字典树)

    题面 题解 为了方便,我们将 xxx 加 1,然后让 aixoraj<xa_i\mathrm{~xor~}a_j<xai​ xor aj​<x . 可以把数插入到字典树中,然后遍历字 ...

  3. SGU 275. To xor or not to xor

    275. To xor or not to xor 链接 题意: 给n个数字,问任意异或后能够组成的最大值. 分析: 线性基模板题. 将每个数的二进制看成一个向量,可以高斯消元得到线性基,复杂度$O( ...

  4. 「题解」300iq Contest 2 B Bitwise Xor

    本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:gym102331B. 题意概述 给你一个长度为 nnn 的序列 aia_iai​,求一个最长的子序列满足所有子序列中的元 ...

  5. 汇编语言中xor指令_汇编语言XOR指令:对两个操作数进行逻辑(按位)异或操作...

    XOR 指令在两个操作数的对应位之间进行(按位)逻辑异或(XOR)操作,并将结果存放在目标操作数中: XOR destination, source XOR 指令操作数组合和大小与 AND 指令及 O ...

  6. 汇编语言中xor指令_汇编语言XOR指令:对两个操作数进行逻辑(按位)异或操作(推荐)...

    汇编语言 汇编语言(assembly language)是一种用于电子计算机.微处理器.微控制器或其他可编程器件的低级语言,亦称为符号语言.在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号 ...

  7. 【NWPU2018 练着玩】入门班day1 枚举贪心[Cloned] F - Claris and XOR (HDU-5661 Claris and XOR )

    题目链接:https://vjudge.net/contest/246949#problem/F 先参考大佬的博客: https://blog.csdn.net/idealism_xxm/articl ...

  8. 2 Chisel基本内容

    在本节中,我们将介绍数字设计的基本组件:组合电路和触发器.这些基本元素可以结合起来,构建更大.更有趣的电路.数字系统通常使用二进制信号,这意味着一个比特或信号只能有两种可能的值之一.这些值通常称为0和 ...

  9. 【转】X分钟速成c++

    X分钟速成Y 其中 Y=c++ C++是一种系统编程语言.用它的发明者, Bjarne Stroustrup的话来说,C++的设计目标是: 成为"更好的C语言" 支持数据的抽象与封 ...

最新文章

  1. python emoji 表情处理过滤
  2. Spring 注入 Filter
  3. c语言6字符宽度和小数位数,2017年计算机二级C语言考点复习
  4. java工程怎么构造成moven_将普通java工程结构改为由maven管理的工程结构域
  5. 跨网段局域网如何互通_如何实现局域网中不同网段互访?企业网必备的高级静态路由...
  6. shell第四次练习
  7. 上周热点回顾(4.30-5.6)
  8. CATIA2018客户端安装错误之提示Runtime VC14 x86失败,返回代码3
  9. 数据分析必备算法(算数平均值,加权平均值,最值,中位数,标准差,时间数据处理 ,数组的轴向汇总, 移动均线 ,卷积(简单概念))
  10. c4isr系统有无服务器,什么是C4ISR系统?
  11. MySQL 8.0.27 下载、安装与配置 超详细教程(Windows64位)
  12. Fiddler证书过期解决
  13. 雷军现身国庆 70 周年阅兵花车!
  14. [ECharts] DEPRECATED: ‘normal‘ hierarchy in itemStyle has been removed since 4.0. All style properti
  15. PostgreSQL修炼之道之PostgreSQL安装与配置(二)
  16. Euclidean algorithm
  17. 这些“新职业”到底好不好干?听听过来人怎么说
  18. vb计算机清除菜单代码,用VB编写简单的程序来清空文档菜单 (转)
  19. 使用素描图像识别人脸
  20. c#: 线程状态和管理之线程的休眠、挂起和中断

热门文章

  1. Ubuntu调用USB摄像头
  2. K-means聚类分析
  3. 详解 Benders 分解与一个算例的 python 代码
  4. iOS 12实现应用内录屏
  5. 华为:决定起诉美国政府
  6. 国际标准电话和手机号码的正确写法
  7. python 老照片修复软件_这款开源的 Python 老照片修复工具火了
  8. 容器技术-Docker 网络01-默认网络
  9. a55计算机主板,高性价比APU主板!AMD A55主板对比评测
  10. 【转载】 恢复百度云同步盘本地误删的文件(2篇)