hdu6085 Rikka with Candies

  1. /** 
  2. HDU 6085 Rikka with Candies 
  3. 题意:A数组n个数,B数组m个数,q个查询, 每次给出一个k,询问有多少对(i,j), 使得Ai % Bj = k, 输出对数对模2的值 
  4.  
  5. 思路:首先,用一个vis数组01方式记录A数组中的数是否出现过,因为有Ai % Bj = k,所以也就是(Ai - k) % Bj = 0,不妨设Ai - k = x,那么Bj则是x的因子,因为 
  6. 是Ai % Bj = k,所以k一定比Bj小,即k的取值范围是[0, Bj)那么可以枚举每个x, 再枚举每个x的因子c,如果c不在B数组出现, 就不管它;否则的话,就在[0, Bj) 
  7. 中的每一个y的值, 对应的加上vis数组中x + y的值,举个例子: 
  8. vis数组中对于6 7 8 9 10 11的出现情况是0 1 1 0 1 0 
  9. 对于x枚举到6,它的因子枚举到3(假设3在B数组存在)的时候,如果有(Ai - k) % 3 = 0,那么就是说,如果k可以取值,k在[0, 3)也就是[0, 2]的范围内,也就转换为 
  10. Ai = x + k, 也就是6 + [0, 2],那么现在只需要看对应取值为k的时候,x + k(Ai)是否存在,如果存在那么k的值即可加上1, 因为此时(Ai, 3)形成了一对模为k的数 
  11. 设res数组是记录余数为k的对数, x=6,c=3的情况如下所示 
  12. vis 6  7  8  9 10 11 
  13.     0  1  1  0  1  0 
  14.  
  15. x+k 6 7 8 
  16.  
  17.   res[0, 1, 2] + vis[x + 0, x + 1, x + 2] 
  18. = res[0, 1, 2] + vis[6, 7, 8] 
  19. = res[0, 1, 2] + [0, 1, 1] 
  20. 就是说res[0]+0, res[1]+1, res[2]+1, 代表有(6+1)%3=0,(6+2)%3=0, 
  21. 即(7, 3)形成了一对余数为1的数,(8, 3)形成了一对余数为2的数 
  22.  
  23. 还有因为直接相加复杂度是O(n^2),题目说只需要模2, 那么可以用异或来写,用异或的话也不可能一个一个来,所以就用将连续若干个01值压缩成一个整数来异或, 
  24. 这样就是O(n^2 / 32), 还有一点就是对于x=0,因为所有正整数都是0的因子,对0分开写,原式就是(0 + k) % Bj = k => k % Bj = k, 就是找在A数组中k存在的前提 
  25. 下,在B数组中找出比k大的数的数量加进res[k]即可 
  26. **/  
  27.   
  28. #include<bits/stdc++.h>  
  29. typedef long long ll;  
  30. const int MAXN = 5e4 + 70;  
  31. using namespace std;  
  32. int a[MAXN], b[MAXN];  
  33. int res[MAXN], vis[MAXN];  
  34. vector<int> G[MAXN];  
  35. ll dig[MAXN][35];  
  36. set<int> st;  
  37. ll ans[MAXN];  
  38.   
  39. void init() {  
  40.     for(int i = 1; i < MAXN - 3; i++) {  
  41.         for(int j = 1; j * j <= i; j++) {  
  42.             if(i % j) continue;  
  43.             G[i].push_back(j);  
  44.             if(i / j != j) G[i].push_back(i / j);  
  45.         }  
  46.         sort(G[i].begin(), G[i].end());  
  47.     }  
  48. }  
  49.   
  50. int main() {  
  51.     init();  
  52.     int n, m, q, k, T;  
  53.     scanf("%d", &T);  
  54.     while(T--) {  
  55.         st.clear();  
  56.         memset(vis, 0, sizeof vis);  
  57.         memset(res, 0, sizeof res);  
  58.         memset(ans, 0, sizeof ans);  
  59.         scanf("%d%d%d", &n, &m, &q);  
  60.         int max_data = -1;  
  61.         for(int i = 0; i < n; i++) {  
  62.             scanf("%d", &a[i]);  
  63.             vis[a[i]] = 1;  
  64.             max_data = max(max_data, a[i]);  
  65.         }  
  66.         for(int i = 0; i < m; i++) {  
  67.             scanf("%d", &b[i]);  
  68.             st.insert(b[i]);  
  69.         }  
  70.         sort(b, b + m);  
  71.         for(int i = 1; i <= 5e4; i++) {  
  72.             ll su = 0;  
  73.             for(int j = 0; j < 32; j++) {  
  74.                 su = su * 2 + vis[i + j];  
  75.                 dig[i][j + 1] = su;  
  76.                 dig[i][j + 1] <<= (31 - j);  
  77.             }  
  78.         }  
  79.   
  80.         for(int i = 1; i <= max_data; i++) {  
  81.             for(int j = 0; j < G[i].size(); j++) {  
  82.                 int x = G[i][j];  
  83.                 if(!st.count(x)) continue;  
  84.                 int L = 0, R = x - 1;  
  85.                 while(L <= R) {  
  86.                     int d = L / 32;  
  87.                     if(i + L > 5e4) break;  
  88.                     if(R - L >= 31) {  
  89.                         ans[d] ^= dig[i + L][32];  
  90.                         L += 32;  
  91.                     } else {  
  92.                         ans[d] ^= dig[i + L][R - L + 1];  
  93.                         L = R + 1;  
  94.                     }  
  95.                 }  
  96.             }  
  97.         }  
  98.   
  99.         int num = 0;  
  100.         for(int i = 0; i * 32 <= 5e4; i++) {  
  101.             int j = (i + 1) * 32 - 1;  
  102.             int t = 32;  
  103.             while(t--) { res[j] = ans[i] & 1; j--; ans[i] >>= 1; }  
  104.         }  
  105.         for(int i = 0; i < n; i++) {  
  106.             int x = a[i];  
  107.             res[x] += m - (upper_bound(b, b + m, x) - b);  
  108.         }  
  109.         while(q--) {  
  110.             scanf("%d", &k);  
  111.             printf("%d\n", res[k] & 1);  
  112.         }  
  113.     }  
  114.     return 0;  
  115. }  
/**
HDU 6085 Rikka with Candies
题意:A数组n个数,B数组m个数,q个查询, 每次给出一个k,询问有多少对(i,j), 使得Ai % Bj = k, 输出对数对模2的值

思路:首先,用一个vis数组01方式记录A数组中的数是否出现过,因为有Ai % Bj = k,所以也就是(Ai - k) % Bj = 0,不妨设Ai - k = x,那么Bj则是x的因子,因为
是Ai % Bj = k,所以k一定比Bj小,即k的取值范围是[0, Bj)那么可以枚举每个x, 再枚举每个x的因子c,如果c不在B数组出现, 就不管它;否则的话,就在[0, Bj)
中的每一个y的值, 对应的加上vis数组中x + y的值,举个例子:
vis数组中对于6 7 8 9 10 11的出现情况是0 1 1 0 1 0
对于x枚举到6,它的因子枚举到3(假设3在B数组存在)的时候,如果有(Ai - k) % 3 = 0,那么就是说,如果k可以取值,k在[0, 3)也就是[0, 2]的范围内,也就转换为
Ai = x + k, 也就是6 + [0, 2],那么现在只需要看对应取值为k的时候,x + k(Ai)是否存在,如果存在那么k的值即可加上1, 因为此时(Ai, 3)形成了一对模为k的数
设res数组是记录余数为k的对数, x=6,c=3的情况如下所示
vis 6  7  8  9 10 11
    0  1  1  0  1  0

x+k 6 7 8

  res[0, 1, 2] + vis[x + 0, x + 1, x + 2]
= res[0, 1, 2] + vis[6, 7, 8]
= res[0, 1, 2] + [0, 1, 1]
就是说res[0]+0, res[1]+1, res[2]+1, 代表有(6+1)%3=0,(6+2)%3=0,
即(7, 3)形成了一对余数为1的数,(8, 3)形成了一对余数为2的数

还有因为直接相加复杂度是O(n^2),题目说只需要模2, 那么可以用异或来写,用异或的话也不可能一个一个来,所以就用将连续若干个01值压缩成一个整数来异或,
这样就是O(n^2 / 32), 还有一点就是对于x=0,因为所有正整数都是0的因子,对0分开写,原式就是(0 + k) % Bj = k => k % Bj = k, 就是找在A数组中k存在的前提
下,在B数组中找出比k大的数的数量加进res[k]即可
**/

#include<bits/stdc++.h>
typedef long long ll;
const int MAXN = 5e4 + 70;
using namespace std;
int a[MAXN], b[MAXN];
int res[MAXN], vis[MAXN];
vector<int> G[MAXN];
ll dig[MAXN][35];
set<int> st;
ll ans[MAXN];

void init() {
    for(int i = 1; i < MAXN - 3; i++) {
        for(int j = 1; j * j <= i; j++) {
            if(i % j) continue;
            G[i].push_back(j);
            if(i / j != j) G[i].push_back(i / j);
        }
        sort(G[i].begin(), G[i].end());
    }
}

int main() {
    init();
    int n, m, q, k, T;
    scanf("%d", &T);
    while(T--) {
        st.clear();
        memset(vis, 0, sizeof vis);
        memset(res, 0, sizeof res);
        memset(ans, 0, sizeof ans);
        scanf("%d%d%d", &n, &m, &q);
        int max_data = -1;
        for(int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
            vis[a[i]] = 1;
            max_data = max(max_data, a[i]);
        }
        for(int i = 0; i < m; i++) {
            scanf("%d", &b[i]);
            st.insert(b[i]);
        }
        sort(b, b + m);
        for(int i = 1; i <= 5e4; i++) {
            ll su = 0;
            for(int j = 0; j < 32; j++) {
                su = su * 2 + vis[i + j];
                dig[i][j + 1] = su;
                dig[i][j + 1] <<= (31 - j);
            }
        }

        for(int i = 1; i <= max_data; i++) {
            for(int j = 0; j < G[i].size(); j++) {
                int x = G[i][j];
                if(!st.count(x)) continue;
                int L = 0, R = x - 1;
                while(L <= R) {
                    int d = L / 32;
                    if(i + L > 5e4) break;
                    if(R - L >= 31) {
                        ans[d] ^= dig[i + L][32];
                        L += 32;
                    } else {
                        ans[d] ^= dig[i + L][R - L + 1];
                        L = R + 1;
                    }
                }
            }
        }

        int num = 0;
        for(int i = 0; i * 32 <= 5e4; i++) {
            int j = (i + 1) * 32 - 1;
            int t = 32;
            while(t--) { res[j] = ans[i] & 1; j--; ans[i] >>= 1; }
        }
        for(int i = 0; i < n; i++) {
            int x = a[i];
            res[x] += m - (upper_bound(b, b + m, x) - b);
        }
        while(q--) {
            scanf("%d", &k);
            printf("%d\n", res[k] & 1);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值