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

探讨了在处理多个有序01串时,通过巧妙运用位运算和字典序概念来解决特定组合问题的方法。文章深入分析了逻辑运算符的性质,并将其与字典序比较相结合,提出了一种新颖的解决方案,用于高效计算满足特定条件的串组合数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

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

思路妙♂妙的题啊

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

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

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

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

#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("");
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值