P4424 [HNOI/AHOI2018]寻宝游戏 思维题
题意:
分析:
-
暴力 \(O(qn\times 2^m)\)
-
正解:
stO 考场上直接切掉这道题的巨佬们,蒟蒻瑟瑟发抖
我们按照操作和数字的组合分清况来讨论:
\(1 \& 0\to 0\) \(1|0\to 1\) \(1 \& 1\to 1\) \(1|1\to 1\)
\(0 \& 0\to 0\) \(0|0\to 0\) \(0\&1\to 0\) \(0|1\to 1\)
我们观察发现第二列和第三列有一些特殊性质:
即 \(|0\) 和 \(\&1\) 的组合不会对原来的值产生任何影响 而 \(|1\) 相当于赋值为 \(1\) ,\(\& 0\) 相当于赋值为 \(0\)
关键的一步来了,我们对操作进行转化,因为操作只有 \(\&\ |\) 两种,和 \(0,1\) 一样,那么我们考虑将操作也转化成 \(0,1\) 序列, 我们把 \(|\) 看做 \(0\) , $& $ 看成 \(1\)
这样一来,我们就能通过操作的大小关系来判断出最后每一位的结果,我们按位 考虑,对于每一位从 \(n\) 到 \(1\) 看成一个从高到低的二进制数字序列,从 \(n\) 到 \(1\) 的操作看成从高到低的二进制操作序列,如果操作序列和数字序列某一位相同,那么没有任何影响,如果操作序列某一位小于数字序列,相当于赋值为 \(1\) ,如果操作序列某一位大于数字序列,相当于赋值为 \(0\) 由于后来的操作会覆盖开始的操作,所以高位的答案会覆盖低位的答案,这和二进制下的大小一样,高位操作优先,所以我们发现,当
- 操作序列小于数字序列,这一位为 \(1\) ( 不能是等于数字序列,是因为最开始这一位为 \(0\) ,必须要有 \(|1\) 操作)
- 操作序列大于等于数字序列,这一位为 \(0\)
然后我们对于原序列每一位,建出数字序列,然后根据查询串的 \(0,1\) 关系,判断出操作序列的上界和下界,之间的每一种操作都可以取到
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define inl inline
#define reg register
using namespace std;
namespace zzc
{
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 5005;
const int mod = 1e9+7;
int n,m,q;
string s[maxn],r;
int rk[maxn],seq[maxn];
char ch;
bool cmp(int x,int y)
{
return s[x]<s[y];
}
long long calc(string &a,string &b)
{
long long bas=1,res=0;
for(int i=n-1;i>=0;i--)
{
res=(res+bas*(a[i]+mod-b[i])%mod)%mod;
bas=bas*2%mod;
}
return res;
}
void work()
{
n=read();m=read();q=read();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
ch=getchar();
while(ch!='0'&&ch!='1') ch=getchar();
s[j]+=ch;
}
}
for(int i=1;i<=m;i++) reverse(s[i].begin(),s[i].end());
for(int i=1;i<=n;i++) s[0]+='0',s[m+1]+='1';
for(int i=1;i<=m+1;i++) seq[i]=i;
sort(seq+1,seq+m+1,cmp);
for(int i=1;i<=m+1;i++) rk[seq[i]]=i;
while(q--)
{
cin>>r;
int dwn=0,upn=m+1;
for(int i=1;i<=m;i++)
{
if(r[i-1]=='0') dwn=max(dwn,rk[i]);
else upn=min(upn,rk[i]);
}
if(dwn>upn) puts("0");
else printf("%lld\n",(1ll*(upn==m+1)?1:0)+calc(s[seq[upn]],s[seq[dwn]]));
}
}
}
int main()
{
zzc::work();
return 0;
}