[bzoj5285][Hnoi2018]寻宝游戏——思维+排序

题目大意:

给定n个m位的二进制数,在每一个数前面补上一个 ∨ \lor ∧ \land ,最前面补上一个0,每次询问最后运算结果为r的方案数为多少。

思路 :

感觉MYY出的题就是不一样。
位数很多,所以应该要想到对于每一位单独考虑。
对于单独每一位,考虑每一个运算符这一位上的影响,不难发现总共有四种情况: ∧ 1 , ∧ 0 , ∨ 1 , ∨ 0 \land 1, \land 0, \lor 1, \lor 0 1,0,1,0,其中 ∧ 1 , ∨ 0 \land 1,\lor 0 1,0对最后的结果不会产生影响,于是我们发现 ∧ 0 \land 0 0会使目前的结果为0, ∨ 1 \lor 1 1会使目前的结果为1,并且每一次的运算独立,和之前的结果并没有任何关系。
于是可以进一步得到,如果最后的结果要为1,那么最后一个有效的运算应该是 ∨ 1 \lor 1 1,如果是0,则最后一个有效的运算应该是 ∧ 0 \land 0 0
于是我们把操作序列的 ∧ \land 看作1, ∨ \lor 看作0,并反转。然后将每一位的序列给从后往前提出来,不难发现当这一位的序列 > > >opt的时候,这一位的结果为1,反之则为0。
于是我们只要对每一位的操作序列排序就好了。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define MREP(i,x) for(int i=beg[x],v;v=to[i],i;i=las[i])
#define debug(x) cout<<#x<<"="<<x<<endl
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj5285.in","r",stdin);
    freopen("bzoj5285.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=1000+10;
const int maxm=5000+10;
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
int n,m,q,rank[maxm];
ll p2[maxn],sum[maxm];
bool t[maxm];
struct node{
    int id;
    bool num[maxn];
    bool operator < (const node & tt) const {
        REP(i,1,n)if(num[i]<tt.num[i])return 1;
        else if(num[i]>tt.num[i])return 0;
        return 0;
    }
}a[maxm];

void init(){
    read(n); read(m); read(q);
    p2[0]=1;
    REP(i,1,n)p2[i]=p2[i-1]*2%mod;
    REP(i,1,n){
        char ch=getchar();
        int cnt=0;
        while(!isdigit(ch))ch=getchar();
        while(isdigit(ch))a[++cnt].num[n-i+1]=ch^'0',ch=getchar();
    }
    REP(i,1,m)a[i].id=i;
    sort(a+1,a+m+1);
    REP(i,1,m)rank[a[i].id]=i;
    //REP(i,1,m)printf("%d\n",rank[i]);
    /*REP(i,1,m){
        //printf("%d\n",a[i].id);
        REP(j,1,n)printf("%d",a[i].num[j]);
        putchar('\n');
    }*/
}

void work(){
    REP(i,1,n)a[m+1].num[i]=1;
    REP(i,1,m+1)REP(j,1,n)if(a[i].num[j])
        sum[i]=(sum[i]+p2[n-j])%mod;
    ++sum[m+1];
    REP(ca,1,q){
        char ch=getchar();
        int cnt=0,L=0,R=m+1;
        while(!isdigit(ch))ch=getchar();
        while(isdigit(ch))t[++cnt]=ch^'0',ch=getchar();
        REP(i,1,m)if(t[i])R=min(R,rank[i]);
        else L=max(L,rank[i]);
        //cout<<L<<" "<<R<<endl;
        if(L>R)printf("0\n");
        else printf("%lld\n",(sum[R]-sum[L]+mod)%mod);
        /*REP(i,1,n)cout<<t[i];
          cout<<endl;*/
    }
}

int main(){
//	File();
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值