多校第五场的1001Rikka with Candies是一道大数据量的计数题。
题意:给定A,B两个序列,有q个询问,每个询问给出一个k,问对于每一k,有多少种情况满足ai % bj = k?A,B的元素个数数据量均为50000,询问次数也为50000
分析:题目时限是3500ms,也就是算法只能是常数很小的n*n的算法才能勉强过掉,所以只能用bitset来加速计数,同时为了加速,必须避免大量使用取模运算,只能用以除数为单位采用加和的方式不断累加,这样可以不使用取模运算,同时这也是我第一次接触bitset,在比赛时并没有AC,之后看了很多博客了解了bitset的用法和大牛们关于这道题的解法,这里推荐一个我看到的博客【2017多校】HDU6085 Rikka with Candies 这个博客和我比赛时想到的思路差不多,但是比赛时我用bitset没有实现。
思路就是枚举余数k, 因为余数为k的条件是ai >= k, bj > k, 所以要从大到小枚举k,同时将上次枚举产生的倍速记录下来,这样bitsetz中记录的就是大于k的bj的倍数,然后和bitset-a做&运算就可以得到bj和a中共有的数字,这样bb & (a >> i)可以得到(a - i) % b = 0的数了,注意bitset右移i位就相当于所有的元素全部减少了i, 这样就可以得到每一个余数的计数答案了
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <cmath>
#include <bitset>
using namespace std;
bitset<50005>a, b, bb, ans;
void solve(int maxk)
{
bb.reset();
ans.reset();
for(int i = maxk; i >= 0; --i)
{
ans[i] = (bb & (a >> i)).count() & 1;
if(b[i])
{
for(int j = 0; j <= 50000; j += i)
bb.flip(j);
}
}
return;
}
int main()
{
int CASE;
scanf("%d", &CASE);
while(CASE--)
{
a.reset();
b.reset();
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
int x;
int maxx = 0;
while(n--)
{
scanf("%d", &x);
a.set(x);
}
while(m--)
{
scanf("%d", &x);
b.set(x);
maxx = max(maxx, x);
}
solve(maxx);
while(q--)
{
scanf("%d", &x);
printf("%c\n", ans[x] ? '1' : '0');
}
}
return 0;
}