[洛谷P4424][HNOI/AHOI2018]尋寶遊戲(bitset)
P4424 [HNOI/AHOI2018]尋寶遊戲
某大學每年都會有一次Mystery Hunt的活動,玩家需要根據設置的線索解謎,找到寶藏的位置,前一年獲勝的隊伍可以獲得這一年出題的機會。
作為新生的你,對這個活動非常感興趣。你每天都要從西向東經過教學樓一條很長的走廊,這條走廊是如此的長,以至於它被人戲稱為infinite corridor。一次,你經過這條走廊時註意到在走廊的墻壁上隱藏著nn 個等長的二進制的數字,長度均為mm 。你從西向東將這些數字記錄了下來,形成一個含有nn 個數的二進制數組a_1,a_2,...,a_na1?,a2?,...,an? 。
很快,在最新的一期的Voo Doo雜誌上,你發現了qq 個長度也為mm 的二進制數r_1,r_2,...,r_qr1?,r2?,...,rq? 。
聰明的你很快發現了這些數字的含義。
保持數組a_1,a_2,...,a_na1?,a2?,...,an? 的元素順序不變,你可以在它們之間插入∧∧ (按位與運算)或者∨∨ (按位或運算)。例如:11011∧00111=0001111011∧00111=00011 ,11011∨00111=1111111011∨00111=11111 。
你需要插入nn 個運算符,相鄰兩個數之前恰好一個,在第一個數的左邊還有一個。如果我們在第一個運算符的左邊補入一個0,這就形成了一個運算式,我們可以計算它的值。與往常一樣,運算順序是從左到右。有趣的是,出題人已經告訴你這個值的可能的集合——Voo Doo雜誌裏的那些二進制數r_1,r_2,...,r_qr1?,r2?,...,rq? ,而解謎的方法,就是對r_1,r_2,...,r_qr1?,r2?,...,rq? 中的每一個值r_iri? ,分別計算出有多少種方法填入這nn 個計算符,使的這個運算式的值是r_iri? 。
然而,infinite corridor真的很長,這意味著數據範圍可能非常大。因此,答案也可能非常大,但是你發現由於謎題的特殊性,你只需要求答案模1000000007的值。
輸入輸出格式
輸入格式:
第一行三個數nn ,mm ,qq ,含義如題所述。
接下來nn 行,其中第ii 行有一個長度為mm 的二進制數,左邊是最高位,表示a_iai? 。
接下來qq 行,其中第ii 行有一個長度為mm 的二進制數,左邊是最高位,表示r_iri? 。
輸出格式:
輸出qq 行,每行一個數,其中的ii 行表示對於r_iri? 的答案。
輸入輸出樣例
輸入樣例#1:
5 5 1 01110 11011 10000 01010 00100 00100
輸出樣例#1:
6
輸入樣例#2:
10 10 3 0100011011 0110100101 1100010100 0111000110 1100011110 0001110100 0001101110 0110100001 1110001010 0010011101 0110011111 1101001010 0010001001
輸出樣例#2:
69 0 5
說明
對於 10% 的數據,n \le 20, m \le 30, q = 1n≤20,m≤30,q=1
對於另外 20 的數據,n \le 1000, m \le 16n≤1000,m≤16
對於另外 40 的數據,n \le 500, m \le 1000n≤500,m≤1000
對於全部的數據1≤n≤1000,1≤m≤5000,1≤q≤10001≤n≤1000,1≤m≤5000,1≤q≤1000
分析:
倒著來推,每次枚舉每個位置是or 還是 and。設每個串為S[i],設每次查詢的串為G
然後有個表 :(設當前枚舉到第i個字符串的第j位,設F[i][j]表示(1 ~ i - 1)操作後第j位為0/1)
————————————
F[i - 1][j] S[i][j] G[j] type
0 / 1 0 0 and
0 1 0 and
1 1 1 and
-1 0 1 and
0 / 1 1 1 or
0 0 0 or
1 0 1 or
-1 1 0 or
————————————
-1表示(1~i - 1)的操作怎麽枚舉都不可能滿足,0/1表示(1 ~ i - 1)的操作隨便枚舉都可以滿足。
這樣我們倒著2^n次方枚舉所有填的可能,當出現-1就剪枝,當全部位都為0/1就可以直接加上2^(i - 1),並剪枝。
這樣剪下來,每一層狀態數最多為2,是不是很神奇。。
然後復雜度就變成了O(nq * (位運算代價))。
使用bitset(要手寫),把位運算代價優化成5000 / 64 = 78
總復雜度O(78nq)
AC代碼:
# include <iostream> # include <cstdio> # include <cstring> using namespace std; typedef unsigned long long llu; const int N = 1012; const int mod = 1e9 + 7; int block,n,m,Q; struct bitset{ llu p[80]; void read() { memset(p,0llu,sizeof p); char i = getchar(); while(!isdigit(i))i = getchar(); for(int j = 0;j < m;j++,i = getchar())p[j / 64] |= (((llu)(i - ‘0‘)) << (j % 64)); } void clear(){for(int i = 0;i <= block;i++)p[i] = 0;} }s[N],f[N],nt,t1,t2,v1,v2,g,c1,c2,que[2][4]; int bac[N],ret,dt,cur,tot,ans; void init() { block = (m - 1) / 64; bac[0] = 1;for(int i = 1;i <= n;i++)bac[i] = (bac[i - 1] << 1) % mod; memset(nt.p,0llu,sizeof nt.p); for(int j = 0;j < m;j++)nt.p[j / 64] |= (1llu << (j % 64)); for(int i = 1;i <= n;i++) { s[i].read(); for(int j = 0;j <= block;j++) f[i].p[j] = s[i].p[j] ^ nt.p[j]; } } int main() { scanf("%d %d %d",&n,&m,&Q); init(); while(Q--) { g.read();dt = ans = cur = 0;que[cur][++dt].clear(); for(int i = n;i;i--) { tot = dt;cur ^= 1;dt = 0; for(int j = 1;j <= tot;j++) { bool f1 = true,f2 = true,l1 = true,l2 = true; for(int k = 0;k <= block;k++) { if(!f1 && !f2)break; if(que[cur ^ 1][j].p[k] == nt.p[k]) { if(f1)t1.p[k] = nt.p[k]; if(f2)t2.p[k] = nt.p[k]; continue; } if(f1)v1.p[k] = s[i].p[k] & (s[i].p[k] ^ g.p[k]); if(f2)v2.p[k] = g.p[k] & (s[i].p[k] ^ g.p[k]); if(f1 && (v1.p[k] & que[cur ^ 1][j].p[k]) != v1.p[k])f1 = false; if(f2 && (v2.p[k] & que[cur ^ 1][j].p[k]) != v2.p[k])f2 = false; if(f1)t1.p[k] = que[cur ^ 1][j].p[k] | (s[i].p[k] ^ v1.p[k]); if(f2)t2.p[k] = que[cur ^ 1][j].p[k] | (f[i].p[k] ^ v2.p[k]); if(f1 && t1.p[k] != nt.p[k])l1 = false; if(f2 && t2.p[k] != nt.p[k])l2 = false; } if(f1) { if(l1)ans = (ans + bac[i - 1]) % mod; else que[cur][++dt] = t1; } if(f2) { if(l2)ans = (ans + bac[i - 1]) % mod; else que[cur][++dt] = t2; } } } for(int j = 1;j <= dt;j++) { bool f1 = true; for(int k = 0;k <= block;k++) { if((que[cur][j].p[k] ^ nt.p[k]) & g.p[k]){f1 = false;break;} } ans += f1; } printf("%d\n",ans); } }
[洛谷P4424][HNOI/AHOI2018]尋寶遊戲(bitset)