bzoj千题计划310:bzoj5285: [Hnoi2018]寻宝游戏(思维题+哈希)

时间:2023-03-09 13:10:53
bzoj千题计划310:bzoj5285: [Hnoi2018]寻宝游戏(思维题+哈希)

https://www.lydsy.com/JudgeOnline/problem.php?id=5285

|0 和 &1 没有影响

若填‘|’,记为0,若填‘&’,记为1

先只考虑最后一位

若要求最后=1

那么最后一个|1 要在最后一个 &0 后面

将n个数的最后一位拿出来构成一个01序列

填在所有数最后一位之前的运算符也拿出来构成一个01序列

将第n个数所在位置视为最高位

对于最高位来说

如果数字序列 和 运算符序列 都是0或都是1,没有影响

如果数字序列是0,运算符序列是1,即最后是 &0,显然不能最终等于1,所以这种运算符序列不合法

如果数字序列是1,运算符序列是0,及最后是|1,显然一定是1,这种运算符序列合法

如果数字序列始终等于运算符序列,因为没有影响,所以最终开始开始的那个0,此运算符序列也不合法

所以

如果这一位要求是1,在只考虑这一位的情况下,合法的运算符序列是 运算符的01序列<数字的01序列

同理可以推出

如果这一位要求是0,在只考虑这一位的情况下,合法的运算符序列是 运算符的01序列>=数字的01序列

即可以得到这样的条件:

设合法的运算符序列为S,第i位的数字序列为Ai

若p的第i位为1,则S<Ai  ①

若p的第i位为0,则S>=Ai ②

记①中最小的Ai为 up,②中最大的Ai为down

所以满足所有位的要求的S的个数=up-down

计算个数开始想的是高精减,题目要求取模,直接哈希即可

#include<cstdio>
#include<algorithm> #define N 5001 using namespace std; const int mod=1e9+; int bit[N]; char s[N];
int has[N]; int sa[N],now[N]; int main()
{
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
bit[]=;
for(int i=;i<=n;++i)
{
bit[i]=bit[i-]<<;
bit[i]-=bit[i]>=mod ? mod : ;
}
for(int i=;i<=m;++i) sa[i]=i;
int c[];
for(int i=;i<=n;++i)
{
c[]=c[]=;
scanf("%s",s+);
for(int j=;j<=m;++j)
{
has[j]=has[j]+(s[j]-'')*bit[i-];
has[j]-=has[j]>=mod ? mod : ;
c[s[j]-'']++;
}
c[]+=c[];
for(int j=m;j;--j) now[c[s[sa[j]]-'']--]=sa[j];
swap(sa,now);
}
int up,down;
for(int t=;t<=q;++t)
{
up=m+; down=;
scanf("%s",s+);
for(int i=;i<=m && up==m+;++i)
if(s[sa[i]]-'') up=i;
for(int i=m;i && !down;--i)
if(!(s[sa[i]]-'')) down=i;
if(up<down)
{
puts("");
continue;
}
up= up==m+ ? bit[n] : has[sa[up]];
down= !down ? : has[sa[down]];
printf("%d\n",(up-down+mod)%mod);
}
}