毒蛇越狱
题解
其实是一道很简单的暴力。
如果我们直接枚举"?“处选择"1"还是选择"0”,时间复杂度达到了
O
(
q
2
∣
?
∣
)
O(q2^{|?|})
O(q2∣?∣)的级别。
显然,当一个串大部分都是"?"的情况下明显是会T掉的。
于是我们考虑能否通过"0"与"1"来求出答案。
我们先对原串进行子集卷积,求出为
f
s
f_{s}
fs表示
s
s
s的子串的值的总和与
g
s
g_{s}
gs表示包含
s
s
s的串的值的总和。
当枚举"1"的时候,我们可以通过对
f
s
f_{s}
fs进行容斥来求解。
先假定所有的问号都为"1"因为此时的
f
s
f_{s}
fs一定是包含又为"1"又为"0"的值的。
根据当前枚举的串元假定的串的"1"相差的个数来进行容斥。
如果相差为奇数,就减去
f
x
f_{x}
fx,为偶数就加上
f
x
f_{x}
fx。
当枚举"0"的时候,其实是与枚举"1"的情况差不多的。只是把枚举的对象换成了"0",更改用
g
g
g而已。
此时我们枚举某一个的复杂度是
O
(
2
∣
x
∣
)
O\left(2^{|x|}\right)
O(2∣x∣)的,由于
l
≤
20
l\leq 20
l≤20,所以
min
(
∣
1
∣
,
∣
0
∣
,
∣
?
∣
)
≤
6
\min(|1|,|0|,|?|)\leq 6
min(∣1∣,∣0∣,∣?∣)≤6。
总时间复杂度为
O
(
2
⌊
n
3
⌋
q
+
n
l
o
g
n
)
O\left(2^{\lfloor\frac{n}{3}\rfloor}q+nlog\,n\right)
O(2⌊3n⌋q+nlogn)。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN (1<<20)+5
#define reg register
typedef long long LL;
const int mo=1e9+7;
template<typename _T>
inline void read(_T &x){
_T f=1;x=0;char s=getchar();
while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int l,q,lim,f[MAXN],g[MAXN],a[MAXN],sum1,sum2,sum3;
char str[25];
int dfs1(int id,int s){
//printf("dfs1%d %d\n",id,s);
if(!id)return a[s];int res=0;
if(str[l-id]!='0')res+=dfs1(id-1,s|(1<<id-1));
if(str[l-id]!='1')res+=dfs1(id-1,s);return res;
}
int dfs2(int id,int s,int fg){
//printf("dfs2:%d %d %d\n",id,s,fg);
if(!id)return f[s]*((fg&1)==(sum2&1)?1:-1);int res=0;
if(str[l-id]!='0')res+=dfs2(id-1,s|(1<<id-1),fg+(str[l-id]=='1'));
if(str[l-id]!='?')res+=dfs2(id-1,s,fg);return res;
}
int dfs3(int id,int s,int fg){
if(!id)return g[s]*((fg&1)==(sum1&1)?1:-1);int res=0;
if(str[l-id]!='1')res+=dfs3(id-1,s,fg+(str[l-id]=='0'));
if(str[l-id]!='?')res+=dfs3(id-1,s|(1<<id-1),fg);return res;
}
signed main(){
read(l);read(q);lim=(1<<l);
for(int i=0;i<lim;i++)scanf("%1d",&a[i]),f[i]=g[i]=a[i];
for(int i=0;i<l;i++)
for(int j=0;j<lim;j++)
if(j&(1<<i))f[j]+=f[j^(1<<i)];
for(int i=0;i<l;i++)
for(int j=0;j<lim;j++)
if(!(j&(1<<i)))g[j]+=g[j|(1<<i)];
for(int i=1;i<=q;i++){
scanf("%s",str);sum1=0,sum2=0,sum3=0;
for(int j=0;j<l;j++)
if(str[j]=='0')sum1++;
else if(str[j]=='1')sum2++;
else if(str[j]=='?')sum3++;
if(sum3<=6)printf("%d\n",dfs1(l,0));
else if(sum2<=6)printf("%d\n",dfs2(l,0,0));
else printf("%d\n",dfs3(l,0,0));
}
return 0;
}