CF1930F 题解

· · 题解

简要题意

维护一个值域为 [0,n) 的集合 a,有 q 次插入,在每次插入之后输出以下式子。

\max_x (\max_{i=1}^{\lvert a \rvert} {(a_i \operatorname{or} x)} - \min_{i=1}^{\lvert a \rvert} {(a_i \operatorname{or} x)})

其中 n \le 2^{22}q \le 10^6,要求强制在线。

题目分析

假设产生答案的 max 项的 is,产生答案的 min 项的 it

考虑在已知 st 的情况下怎么算答案。

先给出结论,答案会是 a_s-a_s \operatorname{and} a_t

至于原因,我们对于每一位单独考虑(因为位与位之间无关)。

  1. a_sa_t 的第 i 位均为 1,显然 x 这一位是什么不要紧,此时贡献为 0
  2. a_sa_t 的第 i 位均为 0,显然 x 这一位是什么也不要紧,此时贡献为 0
  3. a_s 的第 i 位为 0a_t 的这一位为 1,贡献为 -2^ix 这一位填 1 就能使贡献为 0
  4. a_s 的第 i 位为 1a_t 的这一位为 0x 这一位应该填 0,此时贡献为 2^i

由此我们发现答案就是第4种情况的贡献和,即如下式子。

a_s-a_s \operatorname{and} a_t

回到题目,假设新插入的数为 v,则新的 st 只能是以下三种情况。

对于第一种情况,直接继承上一次的答案。

对于第二种,答案为以下式子。

\max_{i=1}^{\lvert a \rvert -1} {(v-v \operatorname{and} a_i)}

于是问题转化成维护给定 v,维护以下式子。

\min_{i=1}^{\lvert a \rvert -1} {(v \operatorname{and} a_i)}

这是一个经典问题,使用按位贪心即可解决。

具体地,只需要记录 f_S 表示 a 中是否有某个数的超集S

```cpp void dfs(int S) { f[S]=1; for(int i=0;i<=n;i++) { if(!(S&(1<<i)) && !(f[S^(1<<i)])) dfs(S^(1<<i)); } } ``` 处理完 $f_S$ 后有如下代码计算$\min_{i=1}^{\lvert a \rvert -1} {(v \operatorname{and} a_i)}$。 ```cpp int now=(1<<22)-1; for(int i=22;i!=-1;i--) { if((v&(1<<i))&&f[now^(1<<i)]) now^=1<<i; } ans=now&v; ``` 大致的思路是对于 $v$ 的第 $i$ 位,如果为 $1$ 就看集合中有没有这一位是 $0$ ,且满足前面已经选择的要求(即 $now$ 所记录的)的数。 计算出结果直接代入式子即可。 第三种情况类似,即求以下式子。 $$\max_{i=1}^{\lvert a \rvert -1} {(a_i-v \operatorname{and} a_i)}$$ 尝试变形,将位运算以外变成与只与 $v$ 相关的样子。 $$\max_{i=1}^{\lvert a \rvert -1} {(v \operatorname{or} a_i - v)}$$ 这个变形是好理解的,从意义上来理解就是 $a_i$ 与 $v$ 中至少有一个是 $1$ 的位数减去 $v$ 上是 $1$ 的位数,即 $a_i$ 的这一位为 $1$,$v$ 的这一位为 $0$ 的位数。 即变成了求如下式子。 $$\max_{i=1}^{\lvert a \rvert -1} {(v \operatorname{or} a_i)}$$ 需要维护子集,进行按位贪心。 于是这道题就做完了。 ## CODE 稍微注意一下我的代码里面的 $n$ 不是题目里的意义,而是实际上的 $\log_2 n$。 ```cpp #include<bits/stdc++.h> using namespace std; const int maxn=1<<23; int n; bool mor[maxn];//超集的f bool les[maxn];//子集的f void dfs1(int x) { mor[x]=1; for(int i=0;i<=n;i++) { if(!(x&(1<<i)) && !(mor[x^(1<<i)])) dfs1(x^(1<<i)); } } void dfs2(int x) { les[x]=1; for(int i=0;i<=n;i++) { if((x&(1<<i)) && !(les[x^(1<<i)])) dfs2(x^(1<<i)); } } int t; int tn; int q; int U; int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&q); tn=n; n=__lg(n); U=(1<<(n+1))-1; for(int i=0;i<=U;i++) les[i]=mor[i]=0; int ans=0; for(int i=1;i<=q;i++) { int v; scanf("%d",&v); v=(v+ans)%tn; int now=U; for(int i=n;~i;i--) { if((v&(1<<i))&&mor[now^(1<<i)]) now^=1<<i; } ans=max(ans,v-(now&v)); // printf("%d ",now&v); now=0; for(int i=n;~i;i--) { if(!(v&(1<<i))&&les[now|1<<i]) now|=1<<i; } ans=max(ans,(now|v)-v); // printf("%d ",now|v); dfs1(v); dfs2(v); printf("%d ",ans); } printf("\n"); } return 0; } ``` 参考资料: [NOIP模拟-----位运算](https://www.cnblogs.com/forever-/p/9736088.html) [位运算](https://www.cnblogs.com/Mars-LG/p/0x01_bit.html)