【HNOI 2018】寻宝游戏
【HNOI 2018】尋寶游戲
Problem
Description
某大學(xué)每年都會(huì)有一次 \(Mystery\ Hunt\) 的活動(dòng),玩家需要根據(jù)設(shè)置的線索解謎,找到寶藏的位置,前一年獲勝的隊(duì)伍可以獲得這一年出題的機(jī)會(huì)。
作為新生的你,對(duì)這個(gè)活動(dòng)非常感興趣。你每天都要從西向東經(jīng)過教學(xué)樓一條很長(zhǎng)的走廊,這條走廊是如此的長(zhǎng),以至于它被人戲稱為 \(Infinite\ Corridor\) 。一次,你經(jīng)過這條走廊的時(shí)候,注意到在走廊的墻壁上隱藏著 \(n\) 個(gè)等長(zhǎng)的二進(jìn)制的數(shù)字,長(zhǎng)度均為 \(m\) 。你從西向東將這些數(shù)字記錄了下來,形成一個(gè)含有 \(n\) 個(gè)數(shù)的二進(jìn)制數(shù)組 \(a_1, a_?2 \cdots, a_?n\) 。
很快,在最新的一期 \(Voo\ Doo\) 雜志上,你發(fā)現(xiàn)了 \(q\) 個(gè)長(zhǎng)度也為 \(m\) 的二進(jìn)制串 \(r_1, r_?2, \cdots, r_?q\) 。
聰明的你很快發(fā)現(xiàn)了這些數(shù)字的含義。
保持?jǐn)?shù)組 \(a_1, a_?2 \cdots, a_n\) 的元素順序不變,你可以在它們之間插入 \(\land\)(按位與運(yùn)算)或者 \(\vee\)(按位或運(yùn)算)兩種二進(jìn)制運(yùn)算符。例如: \(11011 \land 00111 = 00011,11011 \vee 00111 = 11111\) 。
你需要插入恰好 \(n\) 個(gè)運(yùn)算符,相鄰兩個(gè)數(shù)之間恰好一個(gè),在第一個(gè)數(shù)的左邊還有一個(gè)。如果我們?cè)诘谝粋€(gè)運(yùn)算符的左邊補(bǔ)入一個(gè) \(0\) ,這就形成了一個(gè)運(yùn)算式,我們可以計(jì)算它的值。與往常一樣,運(yùn)算順序是從左往右。有趣的是,出題人已經(jīng)告訴你這個(gè)值的可能的集合—— \(Voo Doo\) 雜志里的那一些二進(jìn)制數(shù) \(r_1, r_?2, \cdots, r_q\) ,而解謎的方法,就是對(duì) \(r_1, r_?2, \cdots, r_q\) 中的每一個(gè)值 \(r_i\) ,分別計(jì)算出有多少種方法填入這 \(\mathbf{n}\) 個(gè)運(yùn)算符,使得這個(gè)運(yùn)算式的值是 \(r_i\) 。
然而,\(Infinite\ Corridor\) 真的很長(zhǎng),這意味著數(shù)據(jù)范圍可能非常大。因此,答案也可能非常大,但是你發(fā)現(xiàn)由于謎題的特殊性,你只需要求答案模\(1000000007(10 ^ 9 + 7\) , 一個(gè)質(zhì)數(shù))的值。
Input Format
第一行三個(gè)數(shù)\(n,m,q\),含義如題所述。
接下來\(n\)行,其中第\(i\)行有一個(gè)長(zhǎng)度為\(m\)的二進(jìn)制串,左邊是最高位,表示\(a_i\)。
接下來\(q\)行,其中第\(i\)行有一個(gè)長(zhǎng)度為\(m\)的二進(jìn)制串,左邊是最高位,表示\(r_i\)。
Output Format
輸出\(q\)行,每行一個(gè)數(shù),其中第\(i\)行表示對(duì)應(yīng)于\(r_i\)的答案。
Sample
Input 1
5 5 1 01110 11011 10000 01010 00100 00100Output 1
6Input 2
10 10 3 0100011011 0110100101 1100010100 0111000110 1100011110 0001110100 0001101110 0110100001 1110001010 0010011101 0110011111 1101001010 0010001001Output2
69 0 5Explanation
Explanation for Input 1
有以下且僅有以下六個(gè)運(yùn)算式的值是\(00100_{2}\):(下標(biāo)2表示被標(biāo)識(shí)的數(shù)是二進(jìn)制數(shù))
\(0 \land \ 01110_{2} \land \ 11011_{2} \vee {\ 10000}_{2}{\ \land 01010}_{2}{\ \vee 00100}_{2}\)
\(0 \vee \ 01110_{2}\ {\vee 11011}_{2}\ \land {\ 10000}_{2}{\ \ \land 01010}_{2}{\ \vee 00100}_{2}\)
\(0 \land \ 01110_{2}\ {\vee 11011}_{2} \land {\ 10000}_{2}{\ \land 01010}_{2}{\ \vee 00100}_{2}\)
\(0 \vee \ 01110_{2} \land \ 11011_{2} \land {\ 10000}_{2}{\land \ 01010}_{2}{\ \vee 00100}_{2}\)
\(0 \land \ 01110_{2}\ {\land 11011}_{2} \land {\ 10000}_{2}{\ \land 01010}_{2}{\ \vee 00100}_{2}\)
\(0 \vee \ 01110_{2}\ \vee 11011_{2} \vee {\ 10000}_{2}{\ \ \vee 01010}_{2}{\ \land 00100}_{2}\)
Range
對(duì)于 \(10\%\) 的數(shù)據(jù),\(n \leq 20, m \leq 30, \ q = 1\)
對(duì)于另外 \(20\%\) 的數(shù)據(jù),\(n \leq 1000, m \leq 16\)
對(duì)于另外 \(40\%\) 的數(shù)據(jù),\(n \leq 500, m \leq 1000\)
對(duì)于 \(100\%\) 的數(shù)據(jù),\(1 \leq n \leq 1000, \ 1 \leq m \leq 5000, \ 1 \leq q \leq 1000\)
Algorithm
基數(shù)排序可能算一個(gè)???
Mentality
這個(gè)題看起來超級(jí)嚇人 \(......\) 難度看起來確實(shí)挺大的樣子。
但是我們應(yīng)該冷靜思考,先看看數(shù)據(jù)范圍,\(10^3\) 左右,這大概就只有 \(O(nm)\) 的復(fù)雜度才能過了,最多帶點(diǎn)小常數(shù),連 \(log\) 都帶不起。
辣么怎么辦呢?冷靜分析的話,我們決定單獨(dú)來按位思考,想想每個(gè)二進(jìn)制數(shù)的某一位經(jīng)過運(yùn)算的結(jié)果。
首先,我們發(fā)現(xiàn)每一位的運(yùn)算符只有四種情況:
\(\&0,\&1,|0,|1\)
而我們需要學(xué)會(huì)發(fā)現(xiàn)這四種情況里沒啥用的情況,那就是 \(\&1\) 和 \(|0\) 這兩個(gè)運(yùn)算,它們對(duì)于運(yùn)算結(jié)果沒有任何影響。那么有影響的就只剩下了兩種情況:當(dāng)前位為 \(1\) 的時(shí)候,我們插入 \(|\) 符號(hào)將會(huì)使得結(jié)果必定為 \(1\) ,否則結(jié)果不變;當(dāng)前位為 \(0\) 的時(shí)候,我們插入 \(\&\) 符號(hào)會(huì)使得結(jié)果必定為 \(0\) 。否則結(jié)果不變。
那么我們需要做的就變得很簡(jiǎn)單了:如果詢問中這一位為 \(1\) ,那么我們必需確保最后一個(gè)有效的操作 (\(\&0\) 和 \(|1\)) 為 \(|1\) ,如果這一位為 \(0\) ,則必須確保最后一個(gè)有效操作為 \(\&0\) 或者沒有有效操作 (初始值為 \(0\)) 。
接著就是一個(gè)異常巧妙的轉(zhuǎn)化了:
由于我們發(fā)現(xiàn) \(\&1\) 和 \(|0\) 這兩個(gè)東西對(duì)結(jié)果沒有任何影響,那么我們開開腦洞:我們認(rèn)為 \(\&\) 和 \(1\) 是等價(jià)的,\(|\) 和 \(0\) 是等價(jià)的!
然后腦洞不要停,我們將當(dāng)前位每個(gè)數(shù)前插入運(yùn)算符構(gòu)成一個(gè) \(01\) 串來看,設(shè)這個(gè)串為 \(opt\) ,其中的 \(\&\) 運(yùn)算代表的值就直接設(shè)為 \(1\) ,\(|\) 運(yùn)算代表的值直接設(shè)為 \(0\) 。那么我們發(fā)現(xiàn),將 \(opt\) 與當(dāng)前位的數(shù)構(gòu)成的 \(01\) 串對(duì)其后,如果數(shù)為 \(1\) ,而 \(opt\) 的相同位置為 \(1\) 的話,這一位上的二進(jìn)制值就是相等的,這符合我們等價(jià)的腦洞。
接著,我們會(huì)異常驚喜地發(fā)現(xiàn)一件事情,根據(jù)之前的說法,如果我們要使運(yùn)算的結(jié)果為 \(1\) ,那么最后一個(gè)有效的操作必須為 \(|1\) ,則對(duì)于最后一個(gè)有效操作的位置往后,都必須是等價(jià)操作 (也即無效操作) 。
而最后一個(gè)要求結(jié)果為 \(1\) 的有效操作位置,我們必須填入 \(|\) ,也就是在 \(opt\) 串的相同位置,我們的值為 \(0\) ,而我們此位置往后的位置又全部相等!
想到了什么嗎?對(duì)!如果我們將 \(n\) 個(gè)數(shù)的當(dāng)前位提出來,從后往前構(gòu)成一個(gè)二進(jìn)制數(shù)的話,我們必須大于當(dāng)前 \(opt\) 串代表的二進(jìn)制數(shù)!
舉個(gè)例子:
這是 \(n\) 個(gè)數(shù)的當(dāng)前位:\(1010111\)
若要使結(jié)果為 \(0\) ,假設(shè)我們最后一個(gè)有效操作在第 \(5\) 位,那么最后兩位的運(yùn)算操作都必須為 \(\&\) 運(yùn)算,也就是 \(1\) ,那么我們的操作串如下,\(.\) 代表既可以填 \(1\) 有可以填 \(0\) 。
\(opt:\ ....011\)
當(dāng)前位倒過來之后為:\(1110101\)
操作串倒過來之后為:\(opt:\ 110....\)
\(opt<\) \(n\) 個(gè)數(shù)當(dāng)前位構(gòu)成的串
總結(jié)陳詞,我們?cè)O(shè) \(b_i\) 為 \(n\) 個(gè)數(shù)的第 \(i\) 位提出來再倒過來組成的二進(jìn)制數(shù),那么若 \(r_i\) 的第 \(j\) 位為 \(1\) ,我們必須保證我們的操作串 \(opt< b_j\) ,而我們已經(jīng)證明過了,這是必然要求,那么換而言之,如果 \(opt\ge b_j\) ,這一位的結(jié)果就必定為 \(0\) 。
于是我們的題目變成了好玩的比大小游戲,我們只需要根據(jù) \(m\) 個(gè)和 \(opt\) 有關(guān)的不等式得出結(jié)果就好了 \(hhhg\) 。
那這題的做法出來了!
先預(yù)處理出 \(b\) 數(shù)組并排序。
對(duì)于當(dāng)前詢問,我們找到滿足 \(r_i=0\) 的最大 \(b_i\) ,設(shè)當(dāng)前 \(i\) 為 \(L\) ,滿足 \(r_i=1\) 的最小 \(b_i\) ,設(shè)當(dāng)前 \(i\) 為 \(R\) ,那么最后的答案肯定就是 \(b_R-b_L\) 啊 \(hhhhh\) 。
當(dāng)然注意當(dāng) \(L>R\) 輸出 \(0\) 。
您問我怎么排序?當(dāng)然是基數(shù)排序啊!常數(shù)只有 \(2\) 的 \(O(nm)\) 排序算法。
但是如果您不知道基數(shù)排序我就沒辦法了 \(QwQ\) ,學(xué)一下吧,挺簡(jiǎn)單的。
Code
#include<iostream> #include<cstdio> using namespace std; const int mod=1e9+7; int n,m,Q,tag[2],ra[5002],rb[5002],num[5001],mi[1002],t[5002]; char s[5002],q[5002]; void Mod(int &x){x=(x%mod+mod)%mod;} int main() {cin>>n>>m>>Q;mi[1]=1;for(int i=2;i<=n+1;i++)mi[i]=(mi[i-1]<<1)%mod;//預(yù)處理一下冪for(int i=1;i<=m;i++)ra[i]=i;//排名for(int i=1;i<=n;i++){scanf("%s",s+1);tag[0]=0,tag[1]=m;//基排的桶for(int j=1;j<=m;j++){Mod(num[j]+=s[j]=='1'?mi[i]:0);//處理 b 數(shù)組的值if(s[j]=='0')tag[0]++;}for(int j=m;j>=1;j--)rb[tag[s[ra[j]]-'0']--]=ra[j];//獲取下一輪排名swap(ra,rb);//更新排名}ra[m+1]=m+1;num[m+1]=mi[n+1];//因?yàn)槿绻蟮慕Y(jié)果中沒有 1 ,那么操作串大小上限就是 2^n-1 了。while(Q--){scanf("%s",q+1);int l=0,r=m+1;for(int i=1;i<=m;i++)if(q[ra[i]]=='1'){r=i;break;}//找到最小的for(int i=m;i>=1;i--)if(q[ra[i]]=='0'){l=i;break;}//找到最大的if(l>r)cout<<"0\n";else printf("%d\n",((num[ra[r]]-num[ra[l]])%mod+mod)%mod);} } posted @ 2019-03-09 12:07 洛水·錦依衛(wèi) 閱讀(...) 評(píng)論(...) 編輯 收藏總結(jié)
以上是生活随笔為你收集整理的【HNOI 2018】寻宝游戏的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 神经网络和有限元方法
- 下一篇: 英语foteball足球foteball