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

传送门

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

思路妙♂妙的题啊

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

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

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

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

#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("");
		}
	}
}

转载于:https://www.cnblogs.com/stargazer-cyk/p/11145637.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值