寻宝游戏
Description
某大学每年都会有一次Mystery Hunt的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会。
作为新生的你,对这个活动非常感兴趣。你每天都要从西向东经过教学楼一条很长的走廊,这条走廊是如此的长,以至于它被人戏称为infinite corridor。一次,你经过这条走廊时注意到在走廊的墙壁上隐藏着
n
个等长的二进制的数字,长度均为
很快,在最新的一期的Voo Doo杂志上,你发现了
q
个长度也为
聪明的你很快发现了这些数字的含义。
保持数组
a1,a2,...,an
的元素顺序不变,你可以在它们之间插入
∧
(按位与运算)或者
你需要插入
n
个运算符,相邻两个数之前恰好一个,在第一个数的左边还有一个。如果我们在第一个运算符的左边补入一个0,这就形成了一个运算式,我们可以计算它的值。与往常一样,运算顺序是从左到右。有趣的是,出题人已经告诉你这个值的可能的集合——Voo Doo杂志里的那些二进制数
然而,infinite corridor真的很长,这意味着数据范围可能非常大。因此,答案也可能非常大,但是你发现由于谜题的特殊性,你只需要求答案模1000000007的值。
Input
第一行三个数
n
,
接下来
接下来
q
行,其中第
Output
输出
q
行,每行一个数,其中的
Sample Input
5 3 0
1 2 3 4 5
3 5
5 0
1 4
Sample Output
5
7
6
7
HINT
考场上不会倒推做法,暴力还超时了,正解想到一半就想不动了(而且也不知道那是正解)……
咱是真的菜QAQ
思路:
首先很显然需要按位考虑。
观察每一位,可以发现,
|0
和&1是毫无意义的。
若某一位的值为
1
,则需要满足最后一个
考虑把所有运算符写成一个01串,令所有|运算为
同样考虑蒋所有数某一位的
可以发现,把最右端看成最高位,从高到低比较同为
于是可以发现,能使结果为
考虑对于每一位形成的01串排序。
对于每次询问,首先观察是否合法,即是否有合法的运算符集合对于询问串的每一位均满足要求。
即,对于询问串,所有为
0
的位在原序列中对应位构成的01序列的排名,小于所有为
然后计算答案,可以发现是小于最小的为
1
的位对应01串的合法运算符串数目,减去小于最大的为
于是就做完了~
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1009;
const int M=5009;
const int md=1e9+7;
int n,m,q;
int rk[M],sum[M],tmp[M],pows[N];
char s[M];
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;i++)
rk[i]=i;
pows[0]=1;
for(int i=1;i<=n;i++)
pows[i]=pows[i-1]*2ll%md;
for(int i=1;i<=n;i++)
{
int cnt[]={0,0};
scanf("%s",s+1);
for(int j=1;j<=m;j++)
{
sum[j]=(sum[j]+(ll)(s[j]-'0')*pows[i-1])%md;
cnt[s[j]-'0']++;
}
cnt[1]+=cnt[0];
for(int j=m;j;j--)
tmp[cnt[s[rk[j]]-'0']--]=rk[j];
swap(rk,tmp);
}
reverse(rk+1,rk+m+1);
for(int i=1;i<=q;i++)
{
scanf("%s",s+1);
int lst1=0,fst0=m+1;
for(int j=m;j>=1 && lst1==0;j--)
if(s[rk[j]]-'0')
lst1=j;
for(int j=1;j<=m && fst0==m+1;j++)
if(!(s[rk[j]]-'0'))
fst0=j;
if(lst1>fst0)
{
puts("0");
continue;
}
int c0=lst1>=1?sum[rk[lst1]]:pows[n];
int c1=fst0<=m?sum[rk[fst0]]:0;
printf("%d\n",(c0-c1+md)%md);
}
return 0;
}