题解「HNOI2018 寻宝游戏」

不是很像 ( ext{CN}) 省选的题,个人感觉更像是 ( ext{JOI}) 那类风格的题(?

对于这种位运算题,一上来肯定先想到拆位并找寻位运算的规律。

  • 对于 (vee) 而言,任何一位的二进制数 (vee 0) 都为其自身,(vee 1) 都为 (1)

  • 对于 (wedge) 而言,任何一位的二进制数 (wedge 1) 都为其自身,(wedge 0) 都为 (0)

这提醒我们,对于每一位而言,只有 (wedge 0,vee 1) 能够改变这一位上的状态。

考虑 (m=1) 的情况。若最终运算结果为 (1),最后一个 (vee 1) 必然在 (wedge 0) 之后(也可以不存在 (wedge 0),但一定有 (vee 1));若最终运算结果为 (0),最后一个 (vee 1) 必然在 (wedge 0) 之前(也可以不存在 (vee 1)(wedge 0),因为初始时为 (0))。

但这样还是很难考虑,对模型进行转换。我们发现,插入的每个符号,都对应一个二进制位上的数,除了初始的 (0) 没有符号与之对应。不妨将 (n) 个符号也压缩成一个二进制数,并设 (vee o 0,wedge o 1)。这样设有一个很巧妙的点:当 (vee 0)(wedge 1) 时,按位比较这两个二进制数是一样的,而 (vee 1)(wedge 0) 这些对结果有实质影响的操作,体现在这两个二进制数上就有大小之分。

根据之前的结论,最终运算结果为 (1) 时,最后一个 (vee 1) 必在 (wedge 0) 之后(或者不存在 (wedge 0),只存在 (vee 1))。我们发现将给出的 (n)(1) 位二进制数按 (nsim 1) 从高到低位压缩成一个 (n) 位二进制数 (x)。记 (w) 为操作序列压缩成的二进制数,若 (w<x),那么这一位结果为 (1);否则,若 (wgeq x),这一位结果为 (0)

上述结论的证明如下:

  • 对于 (w eq x) 的情况,根据二进制比较的定义,设 (w)(x)(isim n) 位相等,而在第 (i-1) 位不相等,相等的部分对结果没有影响,只考虑不相等。不相等只存在两种情况,(w) 的第 (i-1) 位为 (0),而 (x) 的第 (i-1) 位为 (1),或 (w) 的第 (i-1) 位为 (1),而 (x) 的第 (i-1)(0)。第一种情况对应了 (vee 1) 晚于 (wedge 0) 的情况,而第二种情况对应了 (wedge 0) 晚于 (vee 1) 的情况。
  • 对于第一种情况,(w<x),最终结果为 (1);对于第二种情况,(w > x),最终结果为 (0)
  • 对于 (w=x) 的情况,相等的部分对结果没有影响,因此最终结果为初始的 (0)

得到上述结论后,对于 (mgeq 2) 的点也非常好想,直接将每一个二进制数拆位,对于每一位得到一个关于 (w) 的不等式,解这个不等式组即可。最终得到 (L leq w < R),答案即为 (R-L),注意对于答案处理边界。

代码写的非常娱乐,对于二进制数排序部分的处理,没有用 ( ext{trie}) 也没有用基排,暴力写了个 ( ext{cmp}) 直接套用 ( ext{sort}),跑的慢死了,看看就好(

#include<cstdio>
#include<algorithm>
typedef long long ll;
const ll mod=1e9+7;
int n;
struct BN {
	int id,a[5005];
}input[1005],c[5005];
char s[5005]; int rev[5005];
inline int min(const int &x,const int &y) {return x<y? x:y;}
inline int max(const int &x,const int &y) {return x>y? x:y;} 
inline bool cmp(const BN &x,const BN &y) {for(register int i=n;i>=1;--i) {if(x.a[i]<y.a[i]) return 1; if(x.a[i]>y.a[i]) return 0;} return 0;}
signed main() {
	int m,q; scanf("%d%d%d",&n,&m,&q);
	for(register int i=1;i<=n;++i) {
		scanf("%s",s+1);
		for(register int j=1;j<=m;++j) input[i].a[j]=s[m-j+1]-'0'; 
	}
	for(register int i=1;i<=m;++i) {
		for(register int j=n;j>=1;--j) {
			c[i].a[j]=input[j].a[i];
		}
		c[i].id=i;
	}
	std::sort(c+1,c+1+m,cmp);
	for(register int i=1;i<=n;++i) c[m+1].a[i]=1;
	for(register int i=1;i<=m;++i) rev[c[i].id]=i;
	for(register int t=1;t<=q;++t) {
		scanf("%s",s+1); int st=0,ed=m+1;
		for(register int i=1;i<=m;++i) {
			if(s[i]=='1') {ed=min(ed,rev[m-i+1]);}
			else {st=max(st,rev[m-i+1]);}
		}
		if(!cmp(c[st],c[ed])) {printf("0
");}
		else {
			ll L=0,R=0;
			for(register int i=n;i>=1;--i) L=((L<<1)|(c[st].a[i]))%mod;
			for(register int i=n;i>=1;--i) R=((R<<1)|(c[ed].a[i]))%mod;
			printf("%lld
",((R+(ed==m+1)-L)%mod+mod)%mod);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/tommy0103/p/13832491.html