写在前边:这里所谓的压位,就是把状态只有0/1两种的值,干脆都不用bool表示了,直接表示成二进制位,这样一个LL可以表示多达64个数据,而一个时钟周期内,LL可以直接处理掉64个数据,而不用像之前的bool运算需要进行64次=64个时钟周期,因此可以把常数变成64分之一,可以优雅地进行暴力。
题意:给出长度为 n 的序列 A ,长度为 m 的序列 B ,保证A中任何两个数字不相同,B也是。ai 表示第 i 个小朋友有 ai 元钱。bi 表示第 i 种糖果的单价是 bi 元。假如小朋友 i 要买 j 号糖果,那么他会根据手中的钱去买最多的 j 糖果,然后剩下 k =ai % bj 元钱肥家,将这个关系表示为(i , j) =k。现在有q次询问。每次询问一个 k,要求给出有多少对(i ,j)=k。答案%2后输出
题解:无论枚举A,还是枚举K啥的,都是n^2级别难以优化,而且用不上%2的条件。那么我们转变思路,枚举总钱数不行,枚举剩的钱数不行,那就枚举花掉的钱。
枚举花掉了 x(>=1) 元钱,因此买的那个玩具的单价肯定是 x 的因子。那么可以预处理出 x 的所有因子。 对于每一个因子 y,表示买 y元/个的玩具 总共花掉了 x 元,那么剩下的钱可能是 0 - y-1。如果B中有 y 这个元素,那么我们可以统计答案,如果没有直接跳过这个 y。
关于统计答案:花掉了 x 元,剩下的钱是0-y-1,那么总钱数就是 x 到 x+y-1 。在这个区间内,某些 ai = x+delta 出现了,那么我们答案为delta的方案就++了。(ai 元买 y元/个的toy),而这个题要输出答案的%2,其实就可以把这个答案压成一位(只有0,1两个值)。一位的加法等价于抑或运算。于是就可以压32(int)或者64(long long)位。每次可以O(1)地更新32或者64个答案。复杂度变成 (n^2)/32或者(n^2)/64,就完美的通过了。。。orz
注意:emmm。。。压位嘛。。。容易自己把自己搞懵逼。。就算调试也比较费劲。。。多写一些题目就可以克服了。orz
Code:
#include<bits/stdc++.h>
using namespace std;
const int MAX = 50050;
typedef long long LL;
LL ans[MAX],dig[MAX][33];
int a[MAX];
int n,m,q;
vector<int> G[MAX];
int anss[MAX];
int sum[MAX];
set<int> b;
void init(){
for (int i=1;i<=MAX;i++){
for (int j=1;j*j<=i;j++){
if (i%j==0){
int temp = i/j;
if (temp==j){
G[i].push_back(j);
}else{
G[i].push_back(j);
G[i].push_back(temp);
}
}
}
sort(G[i].begin(),G[i].end());
}
}
int maxData = -1;
void input(){
memset(a,0,sizeof(a));
memset(sum,0,sizeof(sum));
maxData =-1;
b.clear();
scanf("%d%d%d",&n,&m,&q);
for (int i=0;i<n;i++){
int temp;
scanf("%d",&temp);
a[temp]^=1;
if (temp>maxData){
maxData = temp;
}
}
for (int i = 0;i<=maxData;i++){
int temp =0;
for (int j=1;j<=32;j++){
temp = (temp<<1)+a[i+j-1];
dig[i][j] = temp<<(32-j);
}
}
for (int i=0;i<m;i++){
int temp;
scanf("%d",&temp);
sum[temp]++;
b.insert(temp);
}
for (int i=MAX-1;i>=0;i--){
sum[i]+=sum[i+1];
}
}
void solve(){
memset(ans,0,sizeof(ans));
for (int i=1;i<=maxData;i++){
//i->Money cost
for (int x :G[i]){
//x-> Money per toy
if (!b.count(x)){
continue;
}
int L = 0,R = x-1;
while (L<=R){
if (L+i>maxData){
break;
}
int d = L/32;
if (R-L+1<=32){
ans[d]^=dig[i+L][R-L+1];
break;
}else{
ans[d]^=dig[i+L][32];
L+=32;
}
}
}
}
for (int i=0;i*32<=maxData;i++){
int index = i*32;
int temp = 31;
while (temp>=0){
anss[index+temp] = ans[i]&1;
temp--;
ans[i]>>=1;
}
}
for (int i = 0;i<=maxData;i++){
anss[i]+=sum[i+1]*a[i];
}
while (q--){
int temp;
scanf("%d",&temp);
printf("%d\n",anss[temp]&1);
}
}
int main(){
int t;
scanf("%d",&t);
init();
while (t--){
input();
solve();
}
return 0;
}