BZOJ2728

这道题非常的神,第一步是看出NAND的性质:可以使用他来构造与非或这三种逻辑运算,这就很舒服。然后考虑两个数或的话,本质上是分成三个部分的(a&b,a-(a&b),b-(a&b)),所以我们可以尝试构造线性基,最后的答案只跟线性基里的元素或起来有关。所以说我们尝试枚举每一位,然后将所有数且起来,就得到这一位线性基的元素了,然后大概就可以像数位dp那样瞎算就完了

#include<cstdio>
#include<algorithm>
#include<cstring>

const int N = 1005;

int n, k, tt;
long long l, r, a[N], use, S, ba[N], used;

long long Solve (long long x) {
	if (x < 0) return -1;
	long long ret = 0, ans = 0;
	for (int i = 1; i <= tt; ++i) if (ret + ba[i] <= x) {
		ret += ba[i];
		ans += 1ll << tt - i;
	}
	return ans;
}

int main () {
	scanf("%d%d%lld%lld", &n, &k, &l, &r);
	for (int i = 0; i < n; ++i) scanf("%lld", &a[i]);
	for (int i = k - 1, j; ~i; --i) if (!(used >> i & 1)) {
		for (j = 0, S = (1ll << i + 1) - 1; j < n; ++j) S &= (a[j] >> i & 1) ? a[j] : ~a[j];
		used |= (ba[++tt] = S);
	}
	printf("%lld\n", Solve(r) - Solve(l - 1));
	return 0;
}
posted @ 2017-09-27 10:03  DraZxlnDdt  阅读(110)  评论(0编辑  收藏  举报