七海ノ心象素描

所有的为时已晚都是恰逢其时

Nanami^2 avatar

Author

Nanami^2

大一学生 | 不是很聪明的样子

Telegram

我的频道

不定期推送灵感笔记

t.me/kisanami_blog
联系方式
QQ 群
1072614878
发送邮件

CF Gym102331B Bitwise Xor 题解

Solution

Nanami^2
Nanami^2 Even in the rain.
2023年09月21日
预计阅读 4 分钟
788 字

Solution

引理:一个集合中异或值最小的数对,出现在把所有元素递增排序后相邻的两个数中。

证明:反证法。假设引理不成立,设此时最小数对为 (x,y)(x,y),且存在 yy' 满足 x<y<yx< y' < y。考虑极大的二进制位 kk,满足 xx 的第 kk 位上是 00yy 的第 kk 位上是 11,从而 xyx \oplus y 的第 kk 位是 11

  • 如果 yy' 的第 kk 位是 00,那么 xyx \oplus y' 的第 kk 位是 00,从而 xy<xyx \oplus y' < x \oplus y
  • 如果 yy' 的第 kk 位是 11,那么一定存在极大的二进制位 kk',满足 yy 的这一位是 11yy' 的这一位是 00。然后用类似的方法讨论也能得到 xy<xyx \oplus y' < x \oplus y

所以我们把所有数递增排序。

fif_i 为以 aia_i 结尾的方案数,显然有转移

fi=1+j=1aiajxi1fjf_i = 1+\sum_{j=1 \lor a_i \oplus a_j \ge x}^{i-1} f_j

考虑用 0-1 Trie 优化这个 DP。

fjf_j 挂到 Trie 的叶子上,然后在 Trie 树上跑两步。

aiaj=xa_i \oplus a_j = x 这种情况比较 Trivial\text{Trivial},并且它的处理方法和 aiaj>xa_i \oplus a_j > x 只能说毫不相关。所以先把这种情况单独拎出来,反正就是一个叶子的贡献。

什么时候能直接贡献一棵子树的所有叶子呢?xx 的当前位为 00 时,直接累加与 aia_i 当前位相反的那一棵子树就行,此时一定满足 aiaj>xa_i \oplus a_j > x

然后我们就要使得 aiaja_i \oplus a_j 在已经处理的数位上等于 xx,所以如果 aia_ixx 在当前位相同,我们就往 00 儿子处找,否则就往 11 儿子处找。

// Problem: B. Bitwise Xor
// Contest: Codeforces - 2019 Summer Petrozavodsk Camp, Day 2: 300iq Contest 2 (XX Open Cup, Grand Prix of Kazan)
// URL: https://codeforces.com/gym/102331/problem/B
// Author: yozora0908
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Let's Daze
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define PII pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define SET(a,b) memset(a,b,sizeof(a))
#define CPY(a,b) memcpy(a,b,sizeof(b))
#define rep(i,j,k) for(int i=(j);i<=(k);++i)
#define per(i,j,k) for(int i=(j);i>=(k);--i)
int read() {
int a=0, f=1; char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) a=a*10+c-'0', c=getchar();
return a*f;
}
const int N=3e5+5, mod=998244353;
int n, k, a[N];
int f[N];
struct Trie {
int tot=1, trie[N*60][2], g[N*60];
void insert(int S,int v) {
int x=1;
for(int i=59;~i;--i) {
int a=(S>>i)&1;
if(!trie[x][a]) trie[x][a]=++tot;
x=trie[x][a];
(g[x]+=v)%mod;
}
}
int query(int S) {
int x=1, res=0;
for(int i=59;~i;--i) {
int a=(k>>i)&1, b=(S>>i)&1;
if(a==0) (res+=g[trie[x][b^1]])%=mod;
x=trie[x][a^b];
}
if(x) (res+=g[x])%=mod;
return res;
}
} T;
signed main() {
n=read(), k=read();
rep(i,1,n) a[i]=read();
sort(a+1,a+n+1);
f[1]=1;
T.insert(a[1],f[1]);
rep(i,2,n) {
int D=T.query(a[i]);
f[i]=(1+D)%mod;
T.insert(a[i],f[i]);
}
int ans=0;
rep(i,1,n) (ans+=f[i])%=mod;
printf("%lld\n",ans);
return 0;
}

觉得这篇文章怎么样?

点个赞,让更多人看到!

分享这篇文章

知识因分享而增值

分类

题解

标签

DP
Trie
计数

版权声明:本文作者为 Nanami^2,首发于nanami7.top

遵循 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

评论区

本评论区由 EveSunMaple 自主开发