[集训队作业2018]uoj 425 strings - 搜索 - bitset

这题……
题解:
考虑 O ( 2 n ) O(2^n) O(2n)暴力枚举答案,可以分成两段,先枚举前 a a a位,然后算出目前匹配那些串,设为S。对每个给定的串 s t r i str_i stri求其能匹配后面枚举出来的 2 n − a 2^{n-a} 2na个串中的哪些串,假设叫 b i b_i bi,那么前a位后面能接上的就是S中的串 s t r str str对应的 b b b的并。
因此对 { 1 , ⋯   , q } \{1,\cdots,q\} {1,,q}的所有子集求b的并集,可以预处理的时候分块,这样就会发现那个 2 n q / 32 2^nq/32 2nq/32的部分可以出掉一个常数。通过优秀的调参可以过掉本题。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int BS=(1<<15)+1,QC=11,QS=(1<<10)+10,N=33,Q=110;
bitset<BS> rb[QC][QS],ans;char s[Q][N];int L[QC],R[QC];
inline int ok(char *s,int v,int n)
{
	rep(i,0,n-1) if(s[i]!='?'&&s[i]-'0'!=((v>>i)&1)) return 0;return 1;
}
int main()
{
//	freopen("data.in","r",stdin);
	int n=inn(),q=inn();
	rep(i,1,q) scanf("%s",s[i]);
	int a=min(n,15),b=n-a,qs=10,qc=(q-1)/qs+1;
//	debug(a)sp,debug(b)sp,debug(qs)sp,debug(qc)ln;
	rep(i,1,qc)
	{
		L[i]=(i-1)*qs+1,R[i]=min(i*qs,q);
		rep(j,L[i],R[i]) rep(k,0,(1<<b)-1)
			if(ok(s[j]+a,k,b)) rb[i][1<<(j-L[i])].set(k);
		rep(j,1,(1<<(R[i]-L[i]+1))-1)
		{
			int k=j&(-j);if(j==k) continue;
			rb[i][j]=rb[i][j^k]|rb[i][k];
		}
	}
	int Ans=0;
	rep(i,0,(1<<a)-1)
	{
		ans.reset();
		rep(j,1,qc)
		{
			int t=0;
			rep(k,L[j],R[j]) if(ok(s[k],i,a)) t|=1<<(k-L[j]);
			ans|=rb[j][t];
		}
		Ans+=ans.count();
	}
	return !printf("%d\n",Ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值