【BZOJ5285】[HNOI2018]寻宝游戏(神仙题)

【BZOJ5285】[HNOI2018]寻宝游戏(神仙题)

题面

BZOJ
洛谷

题解

既然是二进制按位的运算,显然按位考虑。
发现这样一个关系,如果是\(or\)的话,只要\(or\ 1\),那么无论前面是啥,操作完之后都是\(1\);同理\(and\ 0\)也是一样,无论前面是什么,操作完都是\(0\)
换个角度来看,如果\(or\ 0\),无论前面是什么,操作完之后都不改变,\(and\ 1\)同理。
那么把\(or\)写成\(0\)\(and\)写成\(1\)
那么,如果当前操作数前面的运算符和某一位上相同,那么就等价于没有进行操作,否则直接知道了运算结果。
假如只有一个二进制位的话,那么就是一个长度为\(n\)\(01\)\(x\),和一个长度为\(n\)的操作串\(opt\)。设最后一位为最高位。
如果最终的结果是\(1\),那么意味着\(x>opt\),否则最终结果为\(0\)。可以手玩验证。
大致的证明的话,如果最后结果是\(1\),意味着最后一个\(or\ 1\)的操作一定要在最后一个\(and\ 0\)的操作之后。再把\(or\)\(and\)换成\(01\)表示就可以得到这个结论。
这样一来就变成了比大小的问题了。我们可以算出\(x\le opt<y\),那么答案就是\(y-x\)
然后一个细节问题,首先提前把每一位按照\(x\)排好序,用基数排序即可。这样子可以直接\(for\)结果为\(0\)的最大值和结果为\(1\)最小值。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 5050
#define MOD 1000000007
char ch[MAX];
int g[1010][MAX],bin[1010],n,m,q;
int c[2],a[MAX],b[MAX],s[MAX],t[MAX];
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	bin[0]=1;for(int i=1;i<=n;++i)bin[i]=2*bin[i-1]%MOD;
	for(int i=1;i<=m;++i)a[i]=i;
	for(int i=1;i<=n;++i)
	{
		scanf("%s",ch+1);c[0]=0;c[1]=m;
		for(int j=1;j<=m;++j)(ch[j]-48)?s[j]=(s[j]+bin[i-1])%MOD:++c[0];
		for(int j=m;j;--j)b[c[ch[a[j]]-48]--]=a[j];
		for(int j=1;j<=m;++j)a[j]=b[j];
	}
	for(int i=1;i<=m;++i)t[i]=s[a[i]];t[m+1]=bin[n];
	while(q--)
	{
		scanf("%s",ch+1);int mx=m+1,mn=0;
		for(int i=m;i;--i)if(ch[a[i]]-48==0){mn=i;break;}
		for(int i=1;i<=m;++i)if(ch[a[i]]-48){mx=i;break;}
		printf("%d\n",mn>mx?0:(t[mx]-t[mn]+MOD)%MOD);
	}
	return 0;
}
posted @ 2019-01-06 22:34  小蒟蒻yyb  阅读(636)  评论(0编辑  收藏  举报