【BZOJ】5285: [Hnoi2018]寻宝游戏 -找规律/拆位

传送门:bzoj5285


当时出题的时候很紧张,是真的紧张,因为觉得实在是太水了。
我当时很怕结果出来十几个AC,全场70。然后就再也没人找我出题了。
当时就不停的想HNOI有什么水题,是不是比我这个更水来安慰自己。
----myy


题解

首先考虑拆出一位来观察性质:

o r   0 or\ 0 or 0 a n d   1 and \ 1 and 1不会使原数变化。
o r   1 or\ 1 or 1 a n d   0 and \ 0 and 0会使原数变为 1 1 1/ 0 0 0,而与原数无关。

可以把操作看做一个01串 T ( 0 − > o r , 1 − > a n d ) T(0->or ,1->and) T(0>or,1>and),而设 S S S为1-n的原串中这一位取出来得到的01串。若 S S S中连续一段 [ l , r ] [l,r] [l,r] T T T相等,则经过这一段操作后仍存在值 = S l − 1 =S_{l-1} =Sl1

若值最后 = 1 =1 =1,则必然 o r   1 or \ 1 or 1出现在 a n d   0 and\ 0 and 0之后。
将第 n n n位作为最高位,要求就变成了 S > T S>T S>T

若值最后 = 0 =0 =0,则 S ≤ T S\leq T ST。直接统计即可。


代码

#include
using namespace std;
const int N=5010,mod=1e9+7;

int n,m,qe,a[N],b[N],s[N],bin[N],c[2];
char q[N];

inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}

int main(){
	int i,j,l,r;
	scanf("%d%d%d",&n,&m,&qe);
	for(i=1;i<=m;++i) a[i]=i;
	bin[1]=1;for(i=2;i<=n;++i) bin[i]=ad(bin[i-1],bin[i-1]);
	for(i=1;i<=n;++i){
		scanf("%s",q+1);c[1]=m;c[0]=0;
		for(j=1;j<=m;++j) q[j]=='1'?(s[j]=ad(s[j],bin[i])):(c[0]++);
		for(j=m;j;--j) b[c[q[a[j]]-'0']--]=a[j];
		for(j=1;j<=m;++j) a[j]=b[j];
    }
    s[m+1]=ad(bin[n],bin[n]);a[m+1]=m+1;
    for(;qe;--qe){
    	scanf("%s",q+1);l=0;r=m+1;
    	for(i=m;i;--i) if(q[a[i]]=='0') {l=i;break;}
    	for(i=1;i<=m;++i) if(q[a[i]]=='1') {r=i;break;}
    	printf("%d\n",l>=r?0:dc(s[a[r]],s[a[l]]));
    }
    return 0;
}

你可能感兴趣的:(妙)