[HNOI2012] 与非(位运算的桥梁)

29 篇文章 0 订阅
本文探讨了如何使用与非(NAND)运算来表示所有其他位运算,并通过位限制条件来构造解决方案。文章介绍了如何通过位运算和数位动态规划(DP)在O(nk^2)的时间复杂度内解决问题,其中n是数字的数量,k是位数。文章还提供了一个C++代码实现来找出在给定限制下可能的位运算结果。
摘要由CSDN通过智能技术生成

与非(nand)是非常强大的位运算,可以表示出所有其他位运算:

not A = A nand A

A and B = not (A nand B)

A or B = (not A) nand (not B)

A xor B = (A and not B) or (not A and B)

相当于我们可以进行任意位运算。

然后我们考虑位与位之间的限制。

如果这 n 个数中每个数第 i 位和第 j 位都完全相同,那么最终运算出来的第 i 位和第 j 位一定相同 。

我们考虑一种构造方案,也就是一个数只在一个位置上为 1,其余都是 0 ,通过 与 运算来得到任意数。

我们把在这一位上为 0 的数全部取反,然后把它们全部与起来,最终得到的第 i 位一定是 1 。同时其他数位与起来不可能是 1 ,否则说明这两个数位完全相同,和前面的推论一致。

所以我们数位 dp 即可解决问题。时间复杂度是 o(nk^2) 。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1005;
const int M=65;
int n,K,fa[N],cnt;
ll L,R,a[N],b[N],c[N],sum[N];
int find(int x) {
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
ll solve(ll x,int k) {
	if(x<0) return 0;
	if(k==0) return 1;
	if(sum[k]<=x) return 1ll<<k;
	ll res=0;
	if(c[k]<=x) res+=solve(x-c[k],k-1);
	res+=solve(x,k-1);
	return res;
}
int main() {
//	freopen("data.in","r",stdin);
//	freopen("own.out","w",stdout);
	scanf("%d%d%lld%lld",&n,&K,&L,&R);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=0;i<K;i++) b[i]=(1ll<<i),fa[i]=i;
	for(int i=0;i<K;i++) {
		for(int j=i+1;j<K;j++) {
			int flg=1;
			for(int k=1;k<=n;k++) {
				if((a[k]>>i&1)^(a[k]>>j&1)) {
					flg=0;
				}
			}
			if(flg && find(i) != find(j)) {
				b[fa[j]] += b[fa[i]], fa[fa[i]] = fa[j];
			}
		}
	}
	for(int i=0;i<K;i++) {
		if(fa[i]==i) {
			c[++cnt]=b[i];
			sum[cnt]=sum[cnt-1]+b[i];
		}
	}
	printf("%lld",solve(R,cnt)-solve(L-1,cnt));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值