2017多校五 1001题 hdu 6085 Rikka with Candies 位运算 bitset

来源:互联网 发布:足彩数据app 编辑:程序博客网 时间:2024/06/01 08:30

题目链接


题意:

给定 a 数组长度 n ,和 b 数组长度 m,与若干个询问 q,每次询问有多少组 (i, j) 满足 a[i] % b[j] == k.


官方题解:

考虑预处理出所有 kk 的答案,问题相当于一个模 22 意义下的 \text{mod}mod 卷积,即给出数组 A,BA,B,将 A_i \times B_jAi×Bj 累加到 w_{i\ \text{mod}\ j}wi mod j 上。

因为 i\ \text{mod}\ j=i-j\lfloor \frac{i}{j} \rfloori mod j=ijji,因此可以枚举 jj 和 k=\lfloor \frac{i}{j} \rfloork=ji。这时相当与把 AA 中区间 [kj,(k+1)j)[kj,(k+1)j)中的所有数依次加到 ww 的 [0,j)[0,j) 区间上。

因为模 22 意义下的加法可以用异或实现,因此可以用压位来优化,这样时间复杂度为 \sum_{i=1}^n \frac{n}{i} \times (32+\frac{i}{32})i=1nin×(32+32i),这约是 \frac{n^2}{32}32n2

注意因为 bitset 不支持提取区间操作,因此需要手写压位。如果不手写也可以用分块的方法来达到同样的复杂度但是常数比手写 bitset 大很多,可能需要一些常数优化才能通过时限。

然而手写压位什么的...0 0 标程写得太厉害了一时半会看不明白...等旅游回去定下心来看Orz

于是乎参考了众大神的 blog,发现分为两类,一类就是压位,另一类里面写得短而精炼。我便暂时就后者好好消化理解了一番。


考虑预处理出所有的 ans[k],因为每个算在 k 上面的答案,其来源只可能是 b 数组中 > k 的元素, 因此考虑从大到小枚举模数,这样每次算 k 时,所有对 k 有所影响的答案已经累计完毕。

事实上,计算是否有 a[i] % b[j] = k, 就相当于计算 a[i] - k 是否是 b[j] 的倍数;如果我们用一个数组 xx 将所有 b[j] 的倍数都标记为 1,则只要看 a[i] - k 的位置是否被标记为 1;再进一步,只要看 xx 数组移动 k 位 是否和 某些 a[ ] 相对应。

既然已经说到了 “移动” 和 “对应”,那么就和位运算挂上钩了,即 “左移” 和 “相与为1”。

用 aa 来将 a[ ] 的对应位 置1,那么上面描述的操作其实就是 (xx << k) & aa, 然后看得到的位串中有多少个 1,即为有多少对 (i, j), 

故 ((xx << i) & aa).count() & 1 即为 (i, j) 对的个数 % 2.

刚才讲的并不严谨,事实上,我们对 b[j] 的倍数进行的操作并不是将其对应位 置1,而是进行翻转,因为其影响可能和之前的相抵消,这一点也很好理解。

这样,大神们的程序就解读完了,实在是太太太精妙了Orz.

压位等我之后再认真学习一发再来补Orz.


Code:

#include <bits/stdc++.h>#define maxn 50010using namespace std;bool ans[maxn], exist[maxn];bitset<maxn> aa, xx;inline int max(int a, int b) { return a > b ? a : b; }void work() {    int n, m, q;    scanf("%d%d%d", &n, &m, &q);    int ma = 0, mb = 0;    aa.reset(); xx.reset();    memset(exist, 0, sizeof(exist));    for (int i = 0; i < n; ++i) {        int x;        scanf("%d", &x);        aa.set(x);        ma = max(ma, x);    }    for (int i = 0; i < m; ++i) {        int x;        scanf("%d", &x);        mb = max(mb, x);        exist[x] = true;    }    for (int i = mb; i >= 0; --i) {        ans[i] = ((xx << i) & aa).count() & 1;        if (exist[i]) {            for (int j = 0; j <= ma; j += i) xx.flip(j);        }    }    while (q--) {        int x;        scanf("%d", &x);        printf("%d\n", ans[x]);    }}int main() {    int T;    scanf("%d", &T);    while (T--) work();    return 0;}


原创粉丝点击