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]\) 就可以随意选择0
或1
了。枚举每一个二进制位 \(\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\) 意义下,
同时,有
发现这个式子就相当于把 \(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;
}