1. 程式人生 > >【BZOJ5285】[HNOI2018]尋寶遊戲(神仙題)

【BZOJ5285】[HNOI2018]尋寶遊戲(神仙題)

【BZOJ5285】[HNOI2018]尋寶遊戲(神仙題)

題面

BZOJ
洛谷

題解

既然是二進位制按位的運算,顯然按位考慮。
發現這樣一個關係,如果是\(or\)的話,只要\(or\ 1\),那麼無論前面是啥,操作完之後都是\(1\);同理\(and\ 0\)也是一樣,無論前面是什麼,操作完都是\(0\)
換個角度來看,如果\(or\ 0\),無論前面是什麼,操作完之後都不改變,\(and\ 1\)同理。
那麼把\(or\)寫成\(0\)\(and\)寫成\(1\)
那麼,如果當前運算元前面的運算子和某一位上相同,那麼就等價於沒有進行操作,否則直接知道了運算結果。
假如只有一個二進位制位的話,那麼就是一個長度為\(n\)

\(01\)\(x\),和一個長度為\(n\)的操作串\(opt\)。設最後一位為最高位。
如果最終的結果是\(1\),那麼意味著\(x>opt\),否則最終結果為\(0\)。可以手玩驗證。
大致的證明的話,如果最後結果是\(1\),意味著最後一個\(or\ 1\)的操作一定要在最後一個\(and\ 0\)的操作之後。再把\(or\)\(and\)換成\(01\)表示就可以得到這個結論。
這樣一來就變成了比大小的問題了。我們可以算出\(x\le opt<y\),那麼答案就是\(y-x\)
然後一個細節問題,首先提前把每一位按照\(x\)排好序,用基數排序即可。這樣子可以直接\(for\)
結果為\(0\)的最大值和結果為\(1\)最小值。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 5050
#define MOD 1000000007
char ch[MAX];
int g[1010][MAX],bin[1010],n,m,q;
int c[2],a[MAX],b[MAX],s[MAX],t[MAX];
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    bin[0]=1;for(int i=1;i<=n;++i)bin[i]=2*bin[i-1]%MOD;
    for(int i=1;i<=m;++i)a[i]=i;
    for(int i=1;i<=n;++i)
    {
        scanf("%s",ch+1);c[0]=0;c[1]=m;
        for(int j=1;j<=m;++j)(ch[j]-48)?s[j]=(s[j]+bin[i-1])%MOD:++c[0];
        for(int j=m;j;--j)b[c[ch[a[j]]-48]--]=a[j];
        for(int j=1;j<=m;++j)a[j]=b[j];
    }
    for(int i=1;i<=m;++i)t[i]=s[a[i]];t[m+1]=bin[n];
    while(q--)
    {
        scanf("%s",ch+1);int mx=m+1,mn=0;
        for(int i=m;i;--i)if(ch[a[i]]-48==0){mn=i;break;}
        for(int i=1;i<=m;++i)if(ch[a[i]]-48){mx=i;break;}
        printf("%d\n",mn>mx?0:(t[mx]-t[mn]+MOD)%MOD);
    }
    return 0;
}