[CF1616H]Keep XOR Low / [CF_GYM102331B]Bitwise Xor

题目

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

第二道题:传送门 to CF

思路

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

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

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

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

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

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

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

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

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

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

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

时间复杂度 O ( n log ⁡ 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 0
		for(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 zero
			res = _dfs(0,0); modAddUp(res,_dfs(1,1));
			return res; // no more things to do
		}
		// if maximum is 0 on this bit
		if(x != y) // already forked
			rep(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 bit
		if(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-compute
		powtwo[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 ⁡ ( a i ⊕ a j ) \min(a_i\oplus a_j) min(aiaj) 的判定问题,和 max ⁡ \max max 很像,唯一的区别是,其判断依据为 “子树内是否有至少两个数” 而非 “是否存在该子树” 了。

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

BONUS:若 a ⩽ b ⩽ c a\leqslant b\leqslant c abc min ⁡ ( a ⊕ b , b ⊕ c ) ⩽ a ⊕ c \min(a\oplus b,b\oplus c)\leqslant a\oplus c min(ab,bc)ac 。证明很简单,考虑 a , c a,c a,c 最高的不相同的 bit \text{bit} bit,由 a ⩽ b ⩽ c a\leqslant b\leqslant c abc 可知 b b b 的前缀与二者都相同;这一位上 b b b 0 0 0 a ⊕ b a\oplus b ab 这一位上是 0 0 0,取 1 1 1 b ⊕ c b\oplus c bc 这一位上是 0 0 0,但 a ⊕ c a\oplus c ac 这一位是 1 1 1,证毕。

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

核心代码

	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 1
			return (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 bit
		int 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 bit
		modAddUp(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 bit
		int res = int(llong(siz[ch[x][0]])*siz[ch[x][1]]%MOD);
		// if minimum is 0 on this bit
		const 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;
	}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/122488028
xor