【BZOJ5285】【HNOI2018】—寻宝游戏(结论题)

传送门

题意:给你nn个有序0101串,你必须在每2个串中间加入&\&或者|,多次询问有多少种方法能得到某一个的串

思路妙♂妙的题啊

分析一下&\&|的性质
 0|\ 0->不变
 1|\ 1->必为1
&0\&0->必为0
&1\&1->不变

考虑对于一个询问串qq和所有串的某一位
如果qq某一位为00
那么这一位最后必定有一个位置为00的地方运算符为&\&
而且这个&\&之后必定没有1|1,即是00的地方为|,是11的地方为&\&
如果qq某一位为11
那么这一位最后必定有一个位置为11的地方运算符为|
而且这个|之后必定没有&0\& 0,即是00的地方为|,是11的地方为&\&
有没有发现什么?
并没有

如果我们把|看做0‘0’11看做1‘1’
那是不是在比较字典序了
如果最后一位为00,那也就是说第一个&0\& 0,即运算符字典序他大的方案
最后一位为11同理,即运算符字典序比他小的方案

那对于qq一位0/10/1来说,字典序/大/小于他的所有方案都合法
那一个串的答案,就是所有合法方案的交集
也就是qq最低为11的位置的答案和最高的一位为00的答案的差
把所有串的同一位看做一个串排个序就可以愉快的解决了

#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("");
		}
	}
}
posted @ 2019-03-01 09:43  Stargazer_cykoi  阅读(122)  评论(0编辑  收藏  举报