$[HNOI/AHOI2018]$寻宝游戏

\([HNOI/AHOI2018]\)寻宝游戏

思维好题。

将每一位领出来,组成\(m\)个长度为\(n\)的二进制数。

考虑将操作转化为\(01\)序列,\((\lor\to0,\land\to1)\)

观察之后发现要使得第\(j\)位运算结果是\(1\)则最后一个\(\lor1\)操作的位置一定要在\(\land0\)后面。

转化一下就是就要求\(x\gt\)操作串。

反之,第\(j\)\(0\)的情况同理可知最后一个\(\land0\)操作的位置一定要在\(\lor1\)后面。

转化一下就是就要求\(x\leq\)操作串。

将领出来的二进制数排个序,找到操作上下界,相减即可。

有一些细节要注意,比如二进制串的最高位来自原串的第\(1\)个还是第\(n\)个。

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
const int p=1e9+7;
const int N=5e3+10;
string S[N];
int n,m,M[N],Rnk[N],Q,Sum[N];
struct Number
{
	int x[N/5+1],Id;
	inline bool operator < (const Number &y) const
		{
			for(int i=1;i<=n;i++)
				if(x[i]!=y.x[i]) return x[i]<y.x[i];
			return 0;
		}
} t[N];
signed main(){
#ifndef ONLINE_JUDGE
    freopen("A.in","r",stdin);//Ans=6
#endif
	n=read(),m=read();Q=read();M[0]=1;
	for(int i=1;i<=n;i++) M[i]=(M[i-1]*2)%p;
	for(int i=1;i<=n;i++) cin>>S[i];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			t[j].x[n-i+1]=S[i][j-1]-'0';
	for(int i=1;i<=m;i++) t[i].Id=i;
	sort(t+1,t+m+1);
	for(int i=1;i<=m;i++) Rnk[t[i].Id]=i;
	for(int i=1;i<=n;i++) t[m+1].x[i]=1;
	for(int i=1;i<=m+1;i++)
		for(int j=1;j<=n;j++)
			if(t[i].x[j]) Sum[i]=(Sum[i]+M[n-j])%p;
	Sum[m+1]++;
	while(Q--)
	{
		int B=0,E=m+1;string X;cin>>X;
		for(int i=1;i<=m;i++) if((X[i-1]-'0')) E=min(E,Rnk[i]);
		for(int i=1;i<=m;i++) if(!(X[i-1]-'0')) B=max(B,Rnk[i]);
		if(B>E) {puts("0");continue;}
		printf("%lld\n",(Sum[E]-Sum[B]+p)%p);
	}
}
posted @ 2019-10-25 22:31  风骨傲天  阅读(218)  评论(0编辑  收藏  举报