版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88069489
洛谷传送门
BZOJ传送门
解析:
先%出题人myy
这道题的转化方向二进制还是可以想到的,但是我没转化出来。。
滚去orz了题解。
分位考虑,一共只有四种操作, ,其中前两种是强行改变,另外两种是屁用没有。
对于某一位来说,要令它为 ,就是要求最后一个 操作在最后一个 操作之后。
我们把位运算序列转化成一个长度为 的二进制串,把后面设置成高位。
将 运算设置成 , 运算设置成 。
将所有原来的二进制数的这一位提取出来成一个 串,同样将最后视作高位。
然后我们滚去高位看,直到这两个串相同的前缀。显然只有两种不会改变结果的操作 ,直到遇到不同的部分。我们发现遇到第一位不同的操作就可以判断当前操作序列是否合法。
比如我们现在需要令当前位为 ,则在高位(即操作序列的后面)出现了 操作就肯定非法,反之,如果是 操作,不管低位(即操作序列的前缀)什么样都肯定合法。
我们发现这个操作实际上就是比较二进制数的大小。
将所有位上的字符串排一个字典序,答案就是最小的结果为 的二进制-最大的结果为 的二进制。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define cs const
cs int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a<b?a-b+mod:a-b;}
inline int mul(int a,int b){return (ll)a*b%mod;}
int n,m,q;
int Pow2[5003];
char ss[5003];
int c[2];
int t[5003],s[5003];
int arr[5003<<1],*a=arr,*b=arr+5003;
signed main(){
scanf("%d%d%d",&n,&m,&q);
for(int re i=Pow2[0]=1;i<=n+1;++i)Pow2[i]=add(Pow2[i-1],Pow2[i-1]);
for(int re i=1;i<=m;++i)a[i]=i;
for(int re i=1;i<=n;++i){
c[0]=0,c[1]=m;scanf("%s",ss+1);
for(int re j=1;j<=m;++j)ss[j]^48?s[j]=add(s[j],Pow2[i-1]):++c[0];
for(int re j=m;j;--j)b[c[ss[a[j]]^48]--]=a[j];swap(a,b);
}
for(int re i=1;i<=m;++i)t[i]=s[a[i]];
t[m+1]=Pow2[n];
int d,u;
while(q--){
scanf("%s",ss+1);
for(d=m;d>=1;--d)if(ss[a[d]]=='0')break;
for(u=1;u<=m;++u)if(ss[a[u]]=='1')break;
cout<<(u<d?0:dec(t[u],t[d]))<<"\n";
}
return 0;
}