[HNOI2018]寻宝游戏

本文详细解析了Luogu4424题的官方解答,介绍了如何将操作序列转化为2进制数进行高效处理,通过手动模拟和合理排序技巧,实现快速求解。感谢寻大爷的帮助,提供了清晰的思路和代码实现。

[Luogu4424] [BZOJ5285]

myy的官方题解

洛谷题解

感谢寻大爷的帮助

\(1.\)把操作序列转化为2进制数,手动模拟的时候把相同性质的放到一起考虑(\(1\)\(|,0\)和 & );有的时候也需要把性质不同的放到一起

\(2.\)确定范围只需要通过两个边界

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
    register LL x=0,f=1;register char c=getchar();
    while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
    while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f*x;
}

const int MAXN=5e3+5;
const int mod=1e9+7;

int a[MAXN],b[MAXN],f[MAXN],g[MAXN],pow[MAXN];
char s[MAXN];
int n,m,Q,cnt[2];

int main(){
    n=read(),m=read(),Q=read();
    pow[0]=1;
    for(int i=1;i<=n;i++) pow[i]=(pow[i-1]*2)%mod;
    for(int i=1;i<=m;i++) a[i]=i;//初值只要都存在就行了
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        cnt[0]=0,cnt[1]=m;
        for(int j=1;j<=m;j++)
            if(s[j]=='1') f[j]=(f[j]+pow[i-1])%mod;
            else cnt[0]++;
        for(int j=m;j>=1;j--) b[cnt[s[a[j]]-'0']--]=a[j];//2进制基数排序,先排低位,再排高位
        swap(a,b);
    }
    for(int i=1;i<=m;i++) g[i]=f[a[i]];
    g[m+1]=pow[n];
    while(Q--){
        // x>op --> 1
        // x<=op --> 0
        // 通过两个临界状态确定范围(还是觉得有点怪)
        scanf("%s",s+1);
        int up=m+1,down=0;
        for(int i=m;i>=1;i--)
            if(s[a[i]]=='0'){down=i;break;}//最大的一个要变成0 --> op要大于这个最大数
        for(int i=1;i<=m;i++)
            if(s[a[i]]=='1'){up=i;break;}//最小的一个要变成1 --> op要小于这个数
        if(up<down) printf("0\n");
        else printf("%d\n",(g[up]-g[down]+mod)%mod);
    }
}

转载于:https://www.cnblogs.com/lizehon/p/10533647.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值