题目描述
某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会。作为新生的你,对这个活动非常感兴趣。你每天都要从西向东经过教学楼一条很长的走廊,这条走廊是如此的长,以至于它被人戏称为 infinite corridor。一次,你经过这条走廊的时候,注意到在走廊的墙壁上隐藏着𝑛个等长的二进制的数字,长度均为𝑚。你从西向东将这些数字记录了下来,形成一个含有𝑛个数的二进制数组 a1,a2,…,an。很快,在最新的一期 Voo Doo 杂志上,你发现了𝑞个长度也为𝑚的二进制串 r1,r2,…,rq。聪明的你很快发现了这些数字的含义。保持数组 a1,a2,…,an 的元素顺序不变,你可以在它们之间插入∧(按位与运算)或者∨(按位或运算)两种二进制运算符。例如:11011∧00111=00011,11011∨00111=11111。你需要插入恰好𝑛个运算符,相邻两个数之间恰好一个,在第一个数的左边还有一个。如果我们在第一个运算符的左边补入一个 0,这就形成了一个运算式,我们可以计算它的值。与往常一样,运算顺序是从左往右。有趣的是,出题人已经告诉你这个值的可能的集合——Voo Doo 杂志里的那一些二进制数 r1,r2,…,rq,而解谜的方法,就是对 r1,r2,…,rq 中的每一个值 ri,分别计算出有多少种方法填入这𝒏个运算符,使得这个运算式的值是 ri。
然而,infinite corridor 真的很长,这意味着数据范围可能非常大。因此,答案也可能非常大,但是你发现由于谜题的特殊性,你只需要求答案模1000000007(109 + 7,一个质数)的值。
Sample Input
10 10 3
0100011011
0110100101
1100010100
0111000110
1100011110
0001110100
0001101110
0110100001
1110001010
0010011101
0110011111
1101001010
0010001001
Sample Output
69
0
5
数据范围
n<=1000 m<=5000 q<=1000
思路
首先观察发现|1会使结果变成1,&0会使结果变成0。而|0和&1没有任何的作用,可以忽略。数据中每一列都是独立的,与其它列无关。又因为操作只有两种,数也是二进制数,而且每一行对应一个操作,可以想到把操作也变为一个二进制数(令&=1,|=0)来和每一列的二进制数比较大小。对于每一列来说,要是最后要求结果为1,那么它的最后一个有效操作一定是|1;如果要求结果为0,那么可以最后一个有效操作为&0或没有有效操作(初始为0).于是,把每一列变成一个倒序排列的二进制数。根据定义,可推出:若要求结果为0,那么操作序列数一定>=当列二进制数;若要求为1,操作序列数一定小于当列二进制数。所以,对于每一个询问,就是求出要求结果为1,二进制数最小的列;以及要求结果为0,二进制数最大的列。可以对每列构成的二叉树进行基数排序。因为只有0,1,所以常数很小。最终复杂度为O(nm)。
***因为要求结果为1时,得到的二进制数是取不到的,相当于是一个左闭右开的区间。所以在定哨兵m+1时最后的值要加1。
Code
#include<cstdio> #include<cstring> using namespace std; const int mod=1000000007; int n,m,q,l,r; char c[5007]; int s[5007][1007],rk[5007],b[2][5007],cnt[2]; void b_sort(){ for (int i=0; i<=m+1; i++) rk[i]=i; for (int i=n; i>0; i--) { cnt[0]=cnt[1]=0; for (int j=1; j<=m; j++) if (s[rk[j]][i]==0) b[0][++cnt[0]]=rk[j]; else b[1][++cnt[1]]=rk[j]; for (int j=1; j<=cnt[0]; j++) rk[j]=b[0][j]; for (int j=1; j<=cnt[1]; j++) rk[j+cnt[0]]=b[1][j]; } } long long ans() { int x=rk[l],y=rk[r]; long long res1=0,res2=0; for (int i=1; i<=n; i++) { res1=res1*2%mod; res1=(res1+s[x][i])%mod; res2=res2*2%mod; res2=(res2+s[y][i])%mod; } if (y==m+1) res2++; return (((res2-res1)%mod)+mod)%mod; } int main(){ scanf("%d%d%d",&n,&m,&q); for (int i=1; i<=n; i++) { scanf("%s",c+1); for (int j=1; j<=m; j++) s[j][n-i+1]=c[j]-'0'; } for (int i=1; i<=n; i++) s[m+1][i]=1; b_sort(); while (q-->0) { scanf("%s",c+1); l=0; r=m+1; for (int i=1; i<=m; i++) if (c[rk[i]]=='1') {r=i; break;} for (int i=m; i>0; i--) if (c[rk[i]]=='0') {l=i; break;} if (l>r) printf("0\n"); else printf("%lld\n",ans()); } return 0; }
来源:https://www.cnblogs.com/yich/p/12016977.html