题意:给你 n n n个有序 01 01 01串,你必须在每2个串中间加入 & \& &或者 ∣ | ∣,多次询问有多少种方法能得到某一个的串
思路妙♂妙的题啊
分析一下
&
\&
&和
∣
|
∣的性质
∣
0
−
|\ 0-
∣ 0−>不变
∣
1
−
|\ 1-
∣ 1−>必为1
&
0
−
\&0-
&0−>必为0
&
1
−
\&1-
&1−>不变
考虑对于一个询问串
q
q
q和所有串的某一位
如果
q
q
q某一位为
0
0
0
那么这一位最后必定有一个位置为
0
0
0的地方运算符为
&
\&
&
而且这个
&
\&
&之后必定没有
∣
1
|1
∣1,即是
0
0
0的地方为
∣
|
∣,是
1
1
1的地方为
&
\&
&
如果
q
q
q某一位为
1
1
1
那么这一位最后必定有一个位置为
1
1
1的地方运算符为
∣
|
∣
而且这个
∣
|
∣之后必定没有
&
0
\& 0
&0,即是
0
0
0的地方为
∣
|
∣,是
1
1
1的地方为
&
\&
&
有没有发现什么?
并没有
如果我们把
∣
|
∣看做
‘
0
’
‘0’
‘0’,
1
1
1看做
‘
1
’
‘1’
‘1’
那是不是在比较字典序了
如果最后一位为
0
0
0,那也就是说第一个
&
0
\& 0
&0,即运算符字典序他大的方案
最后一位为
1
1
1同理,即运算符字典序比他小的方案
那对于
q
q
q一位
0
/
1
0/1
0/1来说,字典序
大
/
小
大/小
大/小于他的所有方案都合法
那一个串的答案,就是所有合法方案的交集
也就是
q
q
q最低为
1
1
1的位置的答案和最高的一位为
0
0
0的答案的差
把所有串的同一位看做一个串排个序就可以愉快的解决了
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
inline int read(){
char ch=getchar();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
return res*f;
}
const ll mod=1e9+7;
const int M=5005;
int n,m,q;
ll sum[M];
char s[M],r[M];
struct line{
int id;
char a[M];
}p[M];
inline bool comp(const line &a,const line &b){
for(int i=1;i<=n;i++){
int f1=a.a[i]-'0',f2=b.a[i]-'0';
if(f1==f2)continue;
return f1<f2;
}
}
inline ll calc(int x){
ll res=0;
for(int i=1;i<=n;i++){
res=res*2%mod;
if(p[x].a[i]=='1')res++;
}
return res;
}
inline ll ksm(ll a,int b,ll res=1){
for(;b;b>>=1,a=a*a%mod){
if(b&1)res=res*a%mod;
}
return res;
}
signed main(){
n=read(),m=read(),q=read();
ll mx=ksm(2,n);
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++){
p[j].a[n-i+1]=s[j];
}
}
for(int i=1;i<=m;i++)p[i].id=i;
sort(p+1,p+m+1,comp);
for(int i=1;i<=m;i++)
sum[i]=calc(i);
for(int i=1;i<=q;i++){
scanf("%s",s+1);
int pos1=0,pos2=0;
for(int j=1;j<=m;j++){
if(s[p[j].id]-'0'){
pos1=j;break;
}
}
for(int j=m;j;j--){
if(!(s[p[j].id]-'0')){
pos2=j;break;
}
}
if(pos1&&pos2&&pos2>pos1)puts("0");
else{
if(pos1)cout<<(sum[pos1]-sum[pos2]+mod)%mod;
else cout<<(mx-sum[pos2]+mod)%mod;
puts("");
}
}
}