与非(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));
}