[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)); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530168.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示