UOJ425【集训队作业2018】strings【分块,常数优化】

给定 (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);
}
原文地址:https://www.cnblogs.com/AThousandMoons/p/14851484.html