题解 P5362 【[SDOI2019]连续子序列】
【题解】[SDOI2019]连续子序列
一道不错的找规律题
UPDATE on 2020.3.11:修复了炸了的LaTex,修正了几处小笔误使得叙述更加清晰明了
题意简述
定义
给定一个01序列
答案对
思路详解
根据这个构造方式,我们第一眼能看出的是,每一次在扩大0110->01101001。
但是这个构造方式仍然过于复杂,不太好处理。注意到数据范围,
考虑另外一种构造方式:每一次扩大
-
- 把S每两位字符分为一组,然后`01`->`0`,`10`->`1`。注意如果某一组是`11`或者`00`,那这种方式行不通。 - 把S从$S_2$到$S_{|S|-1}$每两位字符分为一组,然后`01`->`0`,`10`->`1`。注意如果某一组是`11`或者`00`,那这种方式行不通。 可以证明,对于$\forall S$,若有解,则有且仅有一种方式有解。注意$|S|=2$时仅能用方案一。 -
- 把S从$S_1$到$S_{|S|-1}$每两位字符分为一组,然后`01`->`0`,`10`->`1`。注意如果某一组是`11`或者`00`,那这种方式行不通。 - 把S从$S_2$到$S_{|S|}$每两位字符分为一组,然后`01`->`0`,`10`->`1`。注意如果某一组是`11`或者`00`,那这种方式行不通。注意在这种方式下,找到的字符串第一位是$S_1xor1$。 可以证明,对于$|S|>3$,若有解,则有且仅有一种方式有解。
我们记当前操作得到的字符串是
然后
最后口胡一下,时间复杂度
代码时间
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<string,ll> var;
int t;
const ll mod=1e9+9;
string s;
ll k;
map<var,ll> f;
namespace QiFeng233{
ll calc(string s,ll k){
if(s.size()==1){
if(k==0||k==1||k==2)return k+1;
}else if(s.size()==2){
if(k==0)return 1;
if(k==1)return s[0]==s[1]?1:2;
}else if(s.size()==3){
if(k==0)return s[0]!=s[1]||s[1]!=s[2];
}
var v=make_pair(s,k);
if(f.count(v))return f[v];
string nxt;
ll ans=0;
bool successful=true;
for(int i=0;i<(int)s.size();i+=2){
if(i<(int)s.size()-1&&s[i]==s[i+1]){
successful=false;
break;
}
nxt+=s[i];
}
if(successful)ans+=calc(nxt,s.size()%2?(k>>1):((k+1)>>1)),ans%=mod;
nxt=s[0]^1;
successful=true;
for(int i=1;i<(int)s.size();i+=2){
if(i<(int)s.size()-1&&s[i]==s[i+1]){
successful=false;
break;
}
nxt+=s[i];
}
if(successful)ans+=calc(nxt,s.size()%2?((k+1)>>1):(k>>1)),ans%=mod;
return f[v]=ans;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>t;
while(t--)cin>>s>>k,cout<<QiFeng233::calc(s,k)%mod<<endl;
return 0;
}
反思总结
- 思维题往往不需要多少算法知识,这题就只用了基础算法——分治。
- 思维题要求能跳出思维定势,灵活思考。就像这题,如果不能想到上文提到的构造方式而只是照着题目给你的构造方式死刚,是不会有结果的。
- 面向数据编程,就像看到
k \leq 10^{18} 就能想到\Theta(1) 或者\Theta(\log_2k) 从而想出正解。 - 这个
T.M. 序列全名Thue−Morse序列。仔细一想,好像我以前见过它几次,但是我没一次做出来了/kk - 这题模数是
10^9+9 ,但我一开始模了10^9+7 /kk