给定 (q) 个长为 (n) 的 ( exttt{01?}) 字符串,求其能匹配多少个长为 (n) 的 ( exttt{01}) 字符串。
(nle 30),(qle 100)。
ssss...四苏联人...
把序列对半分,对每个前半部分的序列 (O(q2^m))预处理出其匹配哪些模板串,对每个模板串 (O(q2^{n-m}))预处理出其匹配哪些后半部分的序列。
然后把模板串分块,对每块的子集 (O(q2^{B+n-m}/wB)) 预处理其匹配哪些后半部分的序列。
计算答案的时候枚举前半部分的序列和模板串块,查询其匹配的模板串在该块里匹配哪些后半部分的序列,这是 bitset or 的形式,时间复杂度 (O(q2^n/wB))。
这里是复杂度瓶颈,有一个神必剪枝是,若一堆前半部分的序列匹配的模板串集合相同,那显然只用算一个。
然后瞎 yy 一下,取 (B=10),(m=lfloor n/2 floor) 就完事了。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long LL;
typedef __uint128_t LLL;
const int N = 1<<15;
int n, m, lim, q, now, ans;
struct Bits {
LL x[512];
void set(int p){x[p>>6]|=1ull<<(p&63);}
void reset(){
for(int i = 0;(i<<6) < lim;++ i)
x[i] = 0;
}
int count(){
int res = 0;
for(int i = 0;(i<<6) < lim;++ i)
res += __builtin_popcountll(x[i]);
return res;
}
void operator = (const Bits &o){
for(int i = 0;(i<<6) < lim;++ i)
x[i] = o.x[i];
}
Bits operator | (const Bits &o) const {
Bits res;
for(int i = 0;(i<<6) < lim;++ i)
res.x[i] = x[i] | o.x[i];
return res;
}
void operator |= (const Bits &o){
for(int i = 0;(i<<6) < lim;++ i)
x[i] |= o.x[i];
}
} pre[100], p[10][1024], ret;
string str[100];
LLL f[N];
int main(){
ios::sync_with_stdio(false);
cin >> n >> q;
m = n>>1; lim = 1<<n-m;
for(int i = 0;i < q;++ i)
cin >> str[i];
sort(str, str+q);
q = unique(str, str+q) - str;
for(int i = 0;i < q;++ i){
for(int j = 0;j < (1<<m);++ j){
bool flg = true;
for(int k = 0;k < m && flg;++ k)
flg &= (j>>k&1) != '1'-str[i][k];
if(flg) f[j] |= LLL(1)<<i;
}
for(int j = 0;j < lim;++ j){
bool flg = true;
for(int k = 0;k < n-m && flg;++ k)
flg &= (j>>k&1) != '1'-str[i][k+m];
if(flg) pre[i].set(j);
}
}
sort(f, f + (1<<m));
for(int i = 0;10*i < q;++ i){
int l = 10*i, r = min(10*(i+1), q), all = 1<<r-l;
for(int j = 1;j < all;++ j)
p[i][j] = p[i][j&(j-1)] | pre[__builtin_ctz(j)+l];
}
for(int i = 0;i < (1<<m);++ i){
if(!i || f[i] != f[i-1]){
ret.reset();
for(int j = 0;10*j < q;++ j){
int S = f[i]>>10*j&1023;
if(S) ret |= p[j][S];
}
now = ret.count();
}
ans += now;
}
printf("%d
", ans);
}