• 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);
    }
    
  • 相关阅读:
    作业2(4)求m和n之间的和
    作业2(3)计算x的n次方
    作业3(6)查询水果的单价。有 4 种水果,苹果(apples)、梨(pears)、桔子(oranges)和葡萄(grapes),
    作业3(5)输入五级制成绩(A-E),输出相应的百分制成绩(0-100)区间,要求使用 switch语句。
    作业3(4)循环输入多个年份 year,判断该年是否为闰年。判断闰年的条件是:能被 4 整除但不能被100 整除,或者能被 400 整除。输入-1退出程序执行
    P39-习题2-5
    P39-习题2-7
    计算N户电费
    P39-习题2-4
    P39-习题2-3
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/14851484.html
Copyright © 2020-2023  润新知