bzoj 5285 [Hnoi2018]寻宝游戏

http://www.elijahqi.win/archives/3653
https://www.luogu.org/problemnew/show/P4424

观察发现&1 |0没有意义

如果一个最后的答案是1 那么最后一个1 前面出现或 一定排在最后一个&0的n前面 反过来同理

考虑根据添加的运算符构造0,1序列 设1& 0| 那么

把每一个运算符写成0 1 序列之后从右到左分别看做 从高到低的位置 考虑一个位置如果需要出现1 那么 运算符序列和 数字序列 同时是1 或者同时是0 都是可以的 只有出现 运算符为1 原序列为0 这样是不合法的 那么考虑 只要比原序列字典序小的均可

那么 分别针对每个位置形成的01串进行排序 基数排序即可 然后 找到1位置字典序最小的 找到0位置字典序最大的 先判定是否大于0位置的那个 如果大于则有解

解的个数就是他们之间的一个左闭右开的区间

#include<cstdio>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
const int N=5500;
int n,m,q,bin[N],sa[N],tmp[N],ans[N],cnt[2];
char s[N];
inline void inc(int &x,int v){x=x+v>=mod?x+v-mod:x+v;}
inline int dec(int x,int v){return x-v<0?x-v+mod:x-v;}
int main(){
    freopen("bzoj5285.in","r",stdin);
    scanf("%d%d%d",&n,&m,&q);bin[0]=1;
    for (int i=1;i<=n;++i) bin[i]=bin[i-1]*2%mod;
    for (int i=1;i<=m;++i) sa[i]=i;
    for (int i=0;i<n;++i){
        scanf("%s",s+1);cnt[0]=cnt[1]=0;
        for (int j=1;j<=m;++j){
            if(s[j]=='0') ++cnt[0];
            else ++cnt[1],inc(ans[j],bin[i]);
        }cnt[1]+=cnt[0];
        for (int j=m;j;--j) tmp[cnt[s[sa[j]]-'0']--]=sa[j];swap(sa,tmp);
    }
    for (int owo=1;owo<=q;++owo){
        scanf("%s",s+1);int l=0,r=m+1;
        for (int i=1;i<=m;++i) if (s[sa[i]]-'0') {r=i;break;}
        for (int i=m;i;--i) if (s[sa[i]]-'0'==0) {l=i;break;}
        if(l>=r) {puts("0");continue;}
        printf("%d\n",dec(r==m+1?bin[n]:ans[sa[r]],ans[sa[l]]));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值