bzoj 5285: [Hnoi2018]寻宝游戏

Description

Solution

把输入的 \(n\) 个二进制数看作一个大小为 \(n*m\) 的矩阵
把每一列压成一个二进制数,其中最高位是最下面的元素
然后就有了 \(m\) 个二进制数 \(b_i\),然后逐位考虑

我们把操作序列也变成一个二进制数 \(x\),\(1\)\(\&\),\(0\)\(|\)
那么第 \(i\) 位最后的结果为 \(1\) 当且仅当 \(x<b_i\) (注意最高位是最下面的元素)

然后就是确定 \(x\) 的取值范围了
如果我们把 \(b\) 数组从大到小排序,如果确定了 \(x\) ,那么就相当与把 \(b\) 从某个地方断开,前面的二进制位变成 \(1\),后面的变成 \(0\)

考虑每一个询问: \(r_i\)
首先满足条件的情况一定是:在 \(b\) 数组中,\(r\) 中所有的 \(1\) 位都在 \(0\) 位前面

找到断点 \(i\) 之后,答案就是 \(b[i-1]-b[i]\)

#include<bits/stdc++.h>
using namespace std;
const int N=5010,mod=1000000007;
int n,m,Q,p[N];char s[N];
struct data{
	bool b[1010];int id;
	inline bool operator <(const data &p)const{
		for(int i=n;i>=1;i--)
			if(b[i]!=p.b[i])return b[i]>p.b[i];
		return id<p.id;
	}
}a[N];
bool w[N];
inline int putans(int x){
	int ret=0,t=0;
	for(int i=n;i>=1;i--)
		ret=(1ll*ret*2+a[x-1].b[i])%mod;
	for(int i=n;i>=1;i--)
		t=(1ll*t*2+a[x].b[i])%mod;
	ret=(ret-t+mod)%mod;
	return ret+(x==1);
}
int main(){
  freopen("hunt.in","r",stdin);
  freopen("hunt.out","w",stdout);
  cin>>n>>m>>Q;
  for(int i=1;i<=n;i++){
	  scanf("%s",s+1);
	  for(int j=1;j<=m;j++)a[j].b[i]=s[j]-'0';
  }
  for(int i=1;i<=m;i++)a[i].id=i;
  sort(a+1,a+m+1);
  for(int i=1;i<=m;i++)p[a[i].id]=i;
  for(int i=1;i<=n;i++)a[0].b[i]=1;
  while(Q--){
	  scanf("%s",s+1);
	  for(int i=1;i<=m;i++)w[p[i]]=s[i]-'0';
	  bool t=0,flag=0;
	  for(int i=1;i<=m;i++){
		  if(t && w[i]){flag=1;break;}
		  t=w[i]^1;
	  }
	  if(flag)puts("0");
	  else{
		  for(int i=1;i<=m+1;i++){
			  if(w[i]==0){
				  printf("%d\n",putans(i));
				  break;
			  }
		  }
	  }
  }
  return 0;
}

posted @ 2018-04-18 11:01  PIPIBoss  阅读(374)  评论(0编辑  收藏  举报