HDU 6085 Multi-University Training Contest - Team 5 1001:压位大法好

写在前边:这里所谓的压位,就是把状态只有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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值