P5362 [SDOI2019]连续子序列 思维题
题意:
分析:
不会分析 , 直接找规律:
肉眼分析可以发现, \(T.M\) 序列的生成方式有两种:
- 取反然后复制一遍 \(0110\to 01101001\)
- \(\tiny \color{white}{\text{不}}\)容易发现 \(0110\to 0(1)1(0)1(0)0(1)\)
但是第一个性质不方便用,因为 \(|S|+k\) 不一定是 \(2\) 的整数次幂,所以我们考虑第二种方法
我们发现在后面添加的 \(k\) 的布尔序列也会是 \(01\) ,\(10\) 交替的,所以一定不会存在连续的三个以上的相同字符,所以我们可以反向推导,由第二个性质可以知道 \(0\to 01\) \(1\to 10\) 所以我们每一次可以将 \(S\) 长度减半 \(k\) 也减半,这样递归 \(\log k\) 层就能得到 \(k<=2\) 的情况,然后我们手动判掉 \(n,k<=3\) 的情况,顺便对 \(S,k\) 哈希一下之后记忆化
具体来说就是,每次分两种情况讨论,一种是第一位空出来与前面的拼成一个 \(0\),第二种就是从第一位开始两个一组
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define psl pair<string,long long>
using namespace std;
namespace zzc
{
typedef long long ll;
const ll mod = 1e9+9;
map<psl,ll> f;
long long solve(string s,long long k)
{
if(f[mk(s,k)]) return f[mk(s,k)];
long long n=s.size(),res=0;
if(n==1&&k<=2) return k+1;
if(n==2&&k==0) return 1;
if(n==2&&k==1) return (s[0]==s[1])?1:2;
if(n==3&&k==0) return (s[0]!=s[1]||s[1]!=s[2]);
bool flag=true;
string nxt;
for(int i=0;i<n;i+=2)
{
if(i==n-1||s[i]!=s[i+1]) nxt+=s[i];
else {flag=false;break;}
}
if(flag) res+=solve(nxt,(n&1)?(k>>1):((k+1)>>1))%mod;
nxt=((s[0]-'0')^1)+'0';
flag=true;
for(int i=1;i<n;i+=2)
{
if(i==n-1||s[i]!=s[i+1]) nxt+=s[i];
else {flag=false;break;}
}
if(flag) res+=solve(nxt,(n&1)?((k+1)>>1):(k>>1))%mod;
return f[mk(s,k)]=res%mod;
}
void work()
{
ios::sync_with_stdio(false);
string s;
ll k,t;
cin>>t;
while(t--)
{
cin>>s>>k;
cout<<solve(s,k)%mod<<'\n';
}
}
}
int main()
{
zzc::work();
return 0;
}