BZOJ5285: [Hnoi2018]寻宝游戏
【传送门:BZOJ5285】
简要题意:
PS:and和or运算符在这道题里面是相同优先级的
题解:
显然&1或者|0都是没有意义的操作
我们把运算符也当成01,&表示1,|表示0
这样子对于一个运算式,就可以转成一个01字符串了
我们可以一列一列处理值
若某一列的最终值为1,则需要满足最后一个|1的位置出现在最后一个&0的位置后面
为0,则需要满足最后一个&0的位置要出现在最后一个|1的位置后面
可以发现,使结果为1的运算串为所有字典序小于当前位构成的01串的01串
那么结果为0的运算串就是除了结果为1的运算串之外的01串
那么我们就能得到每一列运算串的范围
然后我们只要取出所有列构成的运算串的范围的公共部分就行了
因为如果直接将01串转成数字太大了,所以如果要比较两个01串的大小就要用基数排序来确定顺序,从而确定大小
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; typedef long long LL; char st[5100]; LL pw[1100],Mod=1e9+7,to1[5100]; int Rank[5100],cnt[2],tt[5100]; int main() { int n,m,q; scanf("%d%d%d",&n,&m,&q); pw[1]=1; for(int i=2;i<=n+1;i++) pw[i]=(pw[i-1]<<1)%Mod; for(int i=1;i<=m;i++) Rank[i]=i; for(int i=1;i<=n;i++) { scanf("%s",st+1); cnt[0]=cnt[1]=0; for(int j=1;j<=m;j++) { to1[j]=(to1[j]+LL(st[j]-'0')*pw[i])%Mod; cnt[st[j]-'0']++; } cnt[1]+=cnt[0]; for(int j=m;j>=1;j--) tt[cnt[st[Rank[j]]-'0']--]=Rank[j]; for(int j=1;j<=m;j++) swap(tt[j],Rank[j]); } reverse(Rank+1,Rank+m+1); while(q--) { scanf("%s",st+1); int l=0,r=m+1; for(int i=m;i>=1;i--) { if(st[Rank[i]]=='1') { r=i;break; } } for(int i=1;i<=m;i++) { if(st[Rank[i]]=='0') { l=i;break; } } if(l==0) printf("%lld\n",to1[Rank[r]]); else if(r==m+1) printf("%lld\n",(pw[n+1]-to1[Rank[l]]+Mod)%Mod); else if(l>=r) printf("%lld\n",(to1[Rank[r]]-to1[Rank[l]]+Mod)%Mod); else printf("0\n"); } return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚