HDU 6085 Multi-University Training Contest

来源:互联网 发布:网络舆情信息相关知识 编辑:程序博客网 时间:2024/06/01 10:52

写在前边:这里所谓的压位,就是把状态只有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 costfor (int x :G[i]){//x-> Money per toyif (!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;}


原创粉丝点击