【BZOJ5285】[HNOI2018]尋寶遊戲(神仙題)
阿新 • • 發佈:2019-01-06
【BZOJ5285】[HNOI2018]尋寶遊戲(神仙題)
題面
題解
既然是二進位制按位的運算,顯然按位考慮。
發現這樣一個關係,如果是\(or\)的話,只要\(or\ 1\),那麼無論前面是啥,操作完之後都是\(1\);同理\(and\ 0\)也是一樣,無論前面是什麼,操作完都是\(0\)。
換個角度來看,如果\(or\ 0\),無論前面是什麼,操作完之後都不改變,\(and\ 1\)同理。
那麼把\(or\)寫成\(0\),\(and\)寫成\(1\)。
那麼,如果當前運算元前面的運算子和某一位上相同,那麼就等價於沒有進行操作,否則直接知道了運算結果。
假如只有一個二進位制位的話,那麼就是一個長度為\(n\)
如果最終的結果是\(1\),那麼意味著\(x>opt\),否則最終結果為\(0\)。可以手玩驗證。
大致的證明的話,如果最後結果是\(1\),意味著最後一個\(or\ 1\)的操作一定要在最後一個\(and\ 0\)的操作之後。再把\(or\)和\(and\)換成\(01\)表示就可以得到這個結論。
這樣一來就變成了比大小的問題了。我們可以算出\(x\le opt<y\),那麼答案就是\(y-x\)。
然後一個細節問題,首先提前把每一位按照\(x\)排好序,用基數排序即可。這樣子可以直接\(for\)
#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; }