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); } }
原文地址:https://www.cnblogs.com/lzdhydzzh/p/8850109.html