[JOI 2018 Final]毒蛇越狱

毒蛇越狱

题解

其实是一道很简单的暴力。

如果我们直接枚举"?“处选择"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(2x)的,由于 l ≤ 20 l\leq 20 l20,所以 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(23nq+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;
}

谢谢

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值