2022.11.14

上午\(QC\)

T1

\(\color{Black}{j}\color{Red}{nmcslh}\) 师傅一顿 \(ACM\),得出了一个树剖做法,然后花了午饭前一节课的时间码完了,并且竟然一遍 \(A\) 了,非常极限,对我来说非常不可思议。

T2

赛时草了个 \(Hash\) 拿了 \(50\)
PS: 赛后发现它需要 Pollard-Rho 不太可订(我是废物)。

下午被昨天的 \(QC\)

昨天 T1

使用某种我方未知的技巧通过它:

  • 预处理:
    • 将所有的数带上它所在的集合塞进一个大数组 \(a\),然后按照数字大小排序。
    • 把所有的询问扒下来,去重后挂到每一个 \(A\) 集合上。
  • 草出答案:
    将每一个数遍历一遍,用一个数组 \(cnt\) 来记录当前每个集合遍历过的数的个数(满足单调性),对于每一个数,我们将它所在的集合拎出来作为 \(A\) 集合,然后暴力遍历这个 \(A\) 集合相关的询问(预处理时挂上去的),更新这个询问的答案。

然后我们会发现这样做是 \(\Theta(nm)\) 的,期望得分 \(60\),但实际得分不知为何草出来 \(70\)

让我们浅加一个孝优化。

显然我们预处理时让询问挂到 \(A,B\) 中更小的上面,这样就可以减少更新次数。
\(\text{Ber}\),过了,我们发现它的复杂度是 \(\Theta(n\sqrt{n})\) 的。

  • 对于 \(\min(\left| A \right|,\left| B \right|) \ge \sqrt{n}\),集合总数就会 \(\le \sqrt{n}\),复杂度 \(\Theta(n\sqrt{n})\)
  • 对于 \(\min(\left| A \right|,\left| B \right|) < \sqrt{n}\),那么显然询问满了也是 \(\Theta(q\sqrt{n})\)

这玩意好像叫根号分治。

昨天 T2

非常炫酷,十分优雅,极致华丽,不失趣味。

题目很简单,求 \(\left[ 1,n-1 \right]\)\(popcount\) 的抑或和,\(n\) 以二进制形式输入,\(n\le2^{3\times10^5}\)
这里用数组 \(a\) 来存 \(n\)\(a\) 的长度为 \(len\)
考虑统计每一个 \(popcount\) 值的贡献,用组合数算出 popcount出现次数。

做法:

从前往后遍历 \(n\) 的每一个二进制位(就是 \(a\) 数组),令当前位为 \(i\)

  • \(a_i = 0\) 直接跳过。
  • \(a_i = 1\) 将这一位置为 0,那么对于二进制位 \(\left[ i+1,len \right]\) 就可以随意选择 01 了。枚举每一个二进制位 \(\left[ i+1,len \right]\)\(popcount \in \left[ 0,len - i \right]\) ,其出现次数即为 \(C_{len-i}^{popcount}\) ,这时的总 \(popcount\) 要加上 \(\left[ 1,i-1 \right]\)
    1 的个数,算进答案即可。

然后我们容易发现,出现次数为偶数的 \(popcount\) 值会两两相互抵消,算出的组合数如果是个偶数我们就可以忽略它了,那么我们来思考一下如何判断组合数的奇偶性。

判断 \(C_m^n\) 的奇偶性:

使用 Lucas 定理。
在模 \(2\) 意义下,

\[\Large{C_m^n = C_{m>>1}^{n>>1} \ast C_{m\%2}^{n\%2}} \]

同时,有

\[\Large{C_0^0 = 1,C_1^0 = 1,C_1^1 = 1,C_0^1 = 0} \]

发现这个式子就相当于把 \(n\)\(m\) 一位一位比对,并且只有 \(C_0^1 = 0\) 时,整个 \(C_m^n\) 会等于 \(0\),否则为 \(1\)
翻译一下,如果存在 \(i\),且 \(n\) 的二进制第 \(i\) 位为 \(1\),且 \(m\) 的二进制 第 \(i\) 位为 \(0\)\(C_m^n\) 就是偶数,否则为奇数。
再翻译一下,\(C_m^n\)偶数当且仅当 \(n\ \& \ m \ne n\)(“\(\&\)”表示按位与)。
非常优雅。

好,我们回到这道题,我们只需要统计出现次数为奇数的 \(popcount\) 即可。对于每个位置 \(i\),我们找出 \(C_{len-i}^{popcount}\) 为奇数的,其实就是找出所有 \(len-i\) 二进制位的子集。

找出 \(n\) 的二进制子集:

代码长这样:

for(int i = n; i; i = (i - 1) & n){
    ...
}

\(i\) 就是我们要的 \(n\) 的子集。

手模并感性理解一下,非常优雅。

那么这题就做完了。
优雅至极。

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
char a[N];
int ans, cnt, len;
int main(){
    scanf("%s", a + 1);
        len = strlen(a + 1);
        for(int i = 1; i <= len; ++i){
            if(a[i] == '1'){
                ans ^= cnt;//要算上 j 为 0 的情况
                for(int j = len - i; j; j = (j - 1) & len - i)
                    ans ^= j + cnt;
                ++cnt;
            }
        }
        printf("%d", ans);
    return 0;
}


posted @ 2022-11-14 15:14  Cotsheep  阅读(20)  评论(0编辑  收藏  举报