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

与非(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));
}
posted @ 2022-04-13 22:22  仰望星空的蚂蚁  阅读(12)  评论(0)    收藏  举报  来源