【SDOI2019】连续子序列(找规律)(爆搜)

传送门


SDOI今年怎么有三道数数题。。。

题解:

仔细一看发现是 T h u e − M o r s e \mathrm{Thue-Morse} ThueMorse序列,我就知道肯定没有什么好事。。。

我不知道在多少道找规律题里面发现过这个序列了,然而没有一次是做出来了的。。。

根据题目的提示,我们考虑这样构造:对于当前串,将 0 0 0替换成 01 01 01并将 1 1 1替换成 10 10 10,于是考虑缩原来的那个串,每次我们可以把问题规模缩小一半。

缩串需要考虑两种方式,直接从头开始两两匹配,或者在串前加一个字符然后两两匹配。

注意到长度大于 3 3 3的串会有唯一的合法缩串的方案,证明的话就是考虑其中一个方案出现了 00 00 00或者 11 11 11,另一个方案就一定会出现 000 000 000或者 111 111 111

然后暴力dfs,并且在串长小于等于3的时候特判就行了。

我们需要处理一个字符后面接上某个长度后是合法子序列的方案数,容易发现 f ( k ) = f ( ⌊ k 2 ⌋ ) + f ( ⌊ k + 1 2 ⌋ ) f(k)=f(\lfloor\frac{k}{2}\rfloor)+f(\lfloor\frac{k+1}{2}\rfloor) f(k)=f(2k)+f(2k+1),记忆化一下,然后在 k ≤ 3 k\leq 3 k3的时候也要特判。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

cs int mod=1e9+9;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
std::unordered_map<ll,int> ma;
inline int get(ll k){
	if(ma.count(k))return ma[k];
	switch(k){
		case 0:return 1;
		case 1:return 2;
		case 2:return 3;
	}return ma[k]=add(get(k>>1),get(k+1>>1));
}

inline bool shrink(cs std::string &s,std::string &ns){
	ns.clear();
	for(int re i=0;i<s.size();i+=2)
	if(s[i]=='0'&&(i+1==s.size()||s[i+1]=='1'))ns+="0";
	else if(s[i]=='1'&&(i+1==s.size()||s[i+1]=='0'))ns+="1";
	else return false;
	return true;
}

inline int calc(cs std::string &s,ll k){
	if(s.size()==1)return get(k);
	if(s.size()==2&&k==0)return 1;
	if(s.size()==2&&k==1)return s[0]==s[1]?1:2;
	if(s.size()==3&&k==0)return s[0]!=s[1]||s[1]!=s[2];
	std::string ns;int ans=0;
	if(shrink(s,ns))Inc(ans,calc(ns,k+!(s.size()&1)>>1));
	if(shrink(std::string(s[0]=='0'?"1":"0")+s,ns))Inc(ans,calc(ns,k+(s.size()&1)>>1));
	return ans; 
}

signed main(){
#ifdef zxyoi
	freopen("sequence.in","r",stdin);
#endif
	std::ios::sync_with_stdio(false);
	int T;std::cin>>T;std::string s;ll k;
	while(T--)std::cin>>s>>k,std::cout<<calc(s,k)<<"\n";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值