「USACO 2020.12 Platinum」Cowmistry
「USACO 2020.12 Platinum」Cowmistry
看到样例解释突然就有了思路.jpg
令$m=\min\{2^t|2^t>k\}$,也就是$k$最高位+1
对于数轴,按照$[im,(i+1)m)$分组,显然跨过分组的数之间异或$\ge m>k$,不合法,扔掉
对于每组,直接看做是$[0,m-1]$,此时令$d=\frac{m} {2}$
将$[0,m-1]$分为$[0,d-1],[d,m-1]$,显然两段之内的数相互异或的结果$<d$,一定合法
也就是说,长度为$d$的段内随意选3个都合法
下面考虑跨过$d$的贡献,显然是一边选一个,一边选两个,此时这些数之间的异或和$\ge d$
以左边选一个为例,令$k’=k-d$
$O(k\log k)$
考虑异或和中一定有$d$这一位,下面只需要对于$i\in[0,d-1]$暴力统计出$[d,m]$中有多少个数$j$满足$i\oplus j\leq k’$
可以前缀和之后$\log k$做到,从高到低依次考虑$k$每一个为1的二进制位$i$,如果查询的数这一位和$x$相同,那么下面可以任意选择
否则将$x\rightarrow x\oplus 2^i$
实现如下
1 | int Que(int x,int *A,int k){ |
得到个数$c$之后,接下来就是为$x$在$c$里选择两个匹配,就是$\sum \frac{c(c-1)} {2}$
由此得到一个$O(k\log k)$做法,可以通过前三档数据
1 | namespace part_klogk{ |
$O(n\log k-n\log ^2 k)$
考虑对于$k’$,问题降阶为求区间$a$中的每一个数 , 在区间$b$中查询合法的个数$cnt_a$
其中$a,b$区间可以认为对应相同的$[0,L-1]$,但是出现元素不同
此时,继续采用上面的方法进行分组,分组对象变为两组区间
令$m’=\min\{2^t|2^t>k’\},d’=\frac{m} {2}$,分组之后
对于$[0,d’-1],[d’,m-1]$,显然两段之内异或$\leq d$,一定合法,加入答案$cnt_a$中
对于跨过区间的贡献,用下面的方法处理
左边区间对于右边区间继续递归进行查询,将得到的结果加上左边区间中数的个数
右边区间继续对于左边区间递归进行查询,将得到的结果加上右边区间中数的个数
问题不断降阶为子问题,会有$\log k$层子问题
每层子问题所有的区间可以分为$O(n)$段 特殊 的段
其他的部分要么完全覆盖要么为空,这些部分可以快速求出
发现答案如果用$(num,cnt)$表示当前查询结果为$num$的个数为$cnt$
每层可以用$O(n)$个不同的二元对表示结果
如此求得后,再自底向上合并得到答案
关于实现
如果真的按照上面的方法,一层层向下分裂区间,会面临着常数大,难以实现的问题
考虑将每个区间$[L_i,R_i]$插入线段树$[0,2^{30}-1]$
显然得到$O(n\log k)$个节点,在底层完全覆盖的节点打上标记
先递归进行第一层的分裂区间操作,对于打上标记的节点可以分成$\frac{r-l+1} {m}$个完全覆盖区间
这些完全覆盖区间的答案可以$O(1)$求出
分裂完成后,每次查询的区间对用$(a,b,L,k,f1,f2)$表示
其中$a$表示查询区间对应节点,$b$表示被查询区间对应节点
$L$表示区间长度,$f1,f2$表示$a,b$是否继承到上层的完全覆盖标记
如此每次合并$(ls_a,rs_b),(rs_a,ls_b)$的查询结果,加上另一边的贡献 即可
当$b$为空或者为完全覆盖时,答案可以$O(1)$得到
当$a$为空时,可以得到空答案
从这样的底层向上合并,每个元素被合并次数$O(\log k)$
没有仔细分析复杂度,应该就是$O(n\log k-n\log ^2 k)$
1 |
|