CF1616H Keep XOR Low(Trie 树上 DP)

CF1616H Keep XOR Low

给你 \(n\) 个整数 \(a_1,a_2,\cdots,a_n\) 和一个整数 \(x\)

你需要求出 \(\{1,2,\cdots,n\}\) 的一个子集 \(S\),满足 \(S\) 中任意两个不同的元素 \(i,j\),满足 \(a_i~{\rm xor}~a_j\le x\)

求选取 \(S\) 的方案数,对 \(998244353\) 取模的结果。

\(1\le n\le 150000,0\le a_i,x< 2^{30}\)

最开始考虑了两个数如果同时被选择,他们前几位的异或值都等于 \(x\),在一个位置上异或值小于 \(x\),后面随意。容易将题目转化到 Trie 树上去。

最重要的一个性质:同时在两棵子树内选择,如果上面已经小于 \(x\) 则可以随意选择,如果还没有小于 \(x\)(前几位异或值等于 \(x\))则继续分讨处理,子树间独立

异或前 \(k\) 位等于 \(x\) 决定了每棵子树只会有一棵其他的子树和它同时选择时会被限制,其他要么是 \(0\) 要么可以随意选择,不妨对于这些“子树对”逐一处理。

\(dp_{p,q}\) 表示在 \(p,q\) 子树内都选择数的集合数量,

  • 如果 \(x\) 这一位为 \(0\),那么只能在 \(p_0,q_0\) 内或 \(p_1,q_1\) 内同时选择,这一位强制相同。
  • 如果 \(x\) 这一位为 \(1\),那么有限制的只有 \(p_0,q_1\)\(p_1,q_0\),其他的都是可选可不选随意,分几类统计答案(\(S_p\) 表示 \(2^{siz_p}-1\)):
    • 如果 \(p,q\) 内都选择了两棵子树,答案 \(dp_{p_0,q_1}\times dp_{p_1,q_0}\)
    • 如果一棵选择两个,一棵选择一个,答案 \(dp_{p_0,q_1}\times(S_{p_1}+S_{q_0})+dp_{p_1,q_0}\times(S_{p_0}+S_{q_1})\)
    • 如果两边都只选择一棵,答案 \(S_{p_0}\times S_{q_0}+S_{p_1}\times S_{q_1}+dp_{p_0,q_1}+dp_{p_1,q_0}\)

递归求解,复杂度 \(\mathcal{O(n)}\)

“加强版”:P7156 [USACO20DEC] Cowmistry P

#define Maxn 150005
#define Maxdot 4500005
#define mod 998244353
int n,lim,All=1;
int a[Maxn],ch[Maxdot][2],hav[Maxdot];
inline void Insert(int x)
{
	int cur=1;
	for(int i=29,nex;i>=0;i--)
	{
		hav[cur]++,nex=(x>>i)&1;
		if(ch[cur][nex]) cur=ch[cur][nex];
		else ch[cur][nex]=++All,cur=All,dep[All]=i;
	}
	hav[cur]++;
}
inline ll Ned(ll y) { return (havlim[y]-1+mod)%mod; }
ll solve(int p,int q,int D)
{
	if(!p || !q) return 0;
	if(D<0) return (p==q)?Ned(hav[p]):(Ned(hav[p])*Ned(hav[q])%mod);
	if((lim>>D)&1)
	{
		if(p==q) return (solve(ch[p][0],ch[p][1],D-1)+Ned(hav[ch[p][0]])+Ned(hav[ch[p][1]]))%mod;
		ll t1=solve(ch[p][0],ch[q][1],D-1),t2=solve(ch[p][1],ch[q][0],D-1);
		ll ret=t1*t2%mod;
		ret=(ret+t1*(Ned(hav[ch[p][1]])+Ned(hav[ch[q][0]]))%mod)%mod;
		ret=(ret+t2*(Ned(hav[ch[p][0]])+Ned(hav[ch[q][1]]))%mod)%mod;
		ret=(ret+Ned(hav[ch[p][1]])*Ned(hav[ch[q][1]])%mod)%mod;
		ret=(ret+Ned(hav[ch[p][0]])*Ned(hav[ch[q][0]])%mod)%mod;
		ret=(ret+t1+t2)%mod;
		return ret;
	}
	else return (solve(ch[p][0],ch[q][0],D-1)+solve(ch[p][1],ch[q][1],D-1))%mod;
}
int main()
{
	n=rd(),lim=rd(),havlim[0];
	for(int i=1;i<=max(n,30);i++) havlim[i]=havlim[i-1]*2ll%mod;
	for(int i=1;i<=n;i++) a[i]=rd(),Insert(a[i]);
	printf("%lld\n",solve(1,1,29));
	return 0;
}
posted @ 2022-11-09 08:57  EricQian06  阅读(80)  评论(0编辑  收藏  举报