2016 华科校赛 B. And

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2016 华科校赛 B. And相关的知识,希望对你有一定的参考价值。

// 2016 华科校赛 B. And

http://acm.hust.edu.cn/problem/show/1672

题目描述

给出 a[1], ..., a[n]。查询 t[1], ... t[m],问有多少 a[] 的子序列的按位与是 t[]。

输入

多组测试,EOF 结束。

n
a[]
m
t[]

1 ≤ n ≤ 1e6
1 ≤ m ≤ (1 << 20)
1 ≤ a[i] ≤ (1 << 20)
1 ≤ t[i] ≤ (1 << 20)

输出

输出一行,每组查询输出一个数,每个数后面一个空格。

来源

sheep

思路

先膜拜一会儿……【三分钟之后】好了开始讲题解。

用分治的思想解决。目的是对于给定的a[],处理出一个数组ans[]。ans[x]的值就是按位与是x的子序列的数目。

考查这个问题的输入和输出,我们建立这样一个分治模型:void work(int cnt[LEN], int ans[LEN]);。其中cnt[]是输入,ans[]是输出。cnt[x]表示a[]中有多少个x;ans[x]表示有多少子序列按位与结果是x。LEN是当前值区间的长度,也就是说当前的wrok解决的问题是[0, LEN)范围内的结果。

对一个取值区间[0, 2L)分析解,最后答案就是[0, 1<<20)的解。

我们发现较大的一半[L, 2L)是容易解决的,因为这里面的数最高位是1,要按位与结果是这里面的数,选择的数也一定得在这个范围内才行(选择了一个[0, L)内的数之后,按位与最高位就是0了)。所以这个部分可以递归使用work解决,具体方法是 work(cnt1[], ans1[]),cnt1[i]是cnt[i + L];ans1[i]将是ans[i + L]。

但是较小的一半是不容易解决的,对i ∈ [0, L),可以选择[0, L)中的数,也可以选择[L, 2L)中的,只要最高位的1被与掉了就可以。既然如此我们不妨考虑整体,不直接计算ans[i],而计算ans[i] + ans[i + L]。这个值的意义就是,无视最高位是多少,按位与的结果的剩下的部分是i的子序列数目。无视最高位,只要把cnt[i]和cnt[i + L]也就是一样的数。调用的方法是work(cnt2[], ans2[]),这里的cnt2[i] = cnt[i] + cnt[i + L],ans2[i]将是ans[i] + ans[i + L]。

分完这两部分,就可以解决ans[]的计算了,对于i ∈ [L, 2L),ans[i] = ans1[i - L];对于i ∈ [0, L),ans[i] = ans2[i] - ans1[i]。

以上是关于2016 华科校赛 B. And的主要内容,如果未能解决你的问题,请参考以下文章

kmp变形,带通配符的kmp——华科校赛E 好题

回忆版华科计算机去年834真题

Little Sub and Traveling(杭师大第十二届校赛E题) 欧拉回路

B. A and B and Compilation Errors

2016HDU校赛

HDU 浙江科技学院校赛2016