题意
有n+1个m位二进制数,其中第一个是0。每次询问会给出一个m位二进制数x,现在可以在这n+1个二进制数之间任意填上and和or,问有多少种填法满足从左到右运算后的结果为x。
n≤1000,m≤5000,q≤1000n≤1000,m≤5000,q≤1000
分析
考场上大家想的基本都是用bitset或用trie的做法,也就是myy说的所谓“不优美”的做法。
这题的正解其实很简单,我们把每一位分开处理。
除了第一个数外,以编号最小的为最低位,把每一个数的第ii位拿出来组成一个位二进制数,设为bibi。
对于所填的运算符,设or为0,and为1,拿出来也会组成一个nn位二进制数,设为。
神奇的地方来了,该位运算结果为1当且仅当bi>xbi>x。
那么我们就可以把bibi从大到小排序,对于每个询问,其答案不为0当且仅当不存在某个1在0的后面。否则就找到第一个0,设为第ii位,那么答案就是。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int N=1005;
const int M=5005;
const int MOD=1000000007;
int n,m,q,bin[N],pos[M];
struct data{int id,s[N],val;}b[M];
char str[M];
bool cmp(data a,data b)
{
for (int i=n-1;i>=0;i--)
if (a.s[i]>b.s[i]) return 1;
else if (a.s[i]<b.s[i]) return 0;
return 0;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
bin[0]=1;
for (int i=1;i<=n;i++) bin[i]=bin[i-1]*2%MOD;
for (int i=0;i<n;i++)
{
scanf("%s",str+1);
for (int j=1;j<=m;j++) b[j].s[i]=str[j]-'0',(b[j].val+=b[j].s[i]*bin[i])%=MOD;
}
for (int i=1;i<=m;i++) b[i].id=i;
std::sort(b+1,b+m+1,cmp);
for (int i=1;i<=m;i++) pos[b[i].id]=i;
b[0].val=bin[n];
while (q--)
{
scanf("%s",str+1);
int mn=m+1,mx=0;
for (int i=1;i<=m;i++)
if (str[i]=='0') mn=std::min(mn,pos[i]);
else mx=std::max(mx,pos[i]);
if (mx>mn) printf("%d\n",0);
else printf("%d\n",(b[mx].val+MOD-b[mn].val)%MOD);
}
return 0;
}
探讨了一道关于二进制数运算填法的问题,通过巧妙地将每一位单独处理并利用排序的方法来高效求解。针对每个询问,快速判断是否有可能的填法,并计算出具体的填法数量。

613

被折叠的 条评论
为什么被折叠?



