P5362 [SDOI2019] 连续子序列 题解--zhengjun
提供一种和其他题解完全不同的解法。
记 为题中给出的序列, 为 取反的结果。
记 表示 。
方便起见, 下标从 开始,其余的串都是从 开始。
这里用 来理解这个序列。
首先考虑找到 子串的性质。
Lemma.1:若 为 的子串且 ,则存在拆分点 ,使得 。
简单地说,就是可以把 拆分成两个串 ,使得 是 的前缀, 是 的前缀( 是字符串翻转)。
证明的话,设 为 ,那么找到 中 最大的 ,那么 就是一个合法的拆分点。简单地说,就是找到进位最大的一次,后面的是 的前缀,前面的翻转是 的前缀。
Lemma.2:若 是 的子串且 ,那么 的拆分点至多只有两个。
你可以考虑把长度 的串都找一遍拆分点并输出,发现这个性质。
证明的话考虑反证就行了,大家有兴趣可以自己证明。
同时,如果你打过拆分点的表,会发现如果 有两个拆分点 ,那么 ,这个很好理解,中间这一段正反都要是 的前缀。
有了这些性质,你会发现一个串 是 的子串的充要条件就是 或 存在拆分点。
的情况只需要特判 的情况即可,下面考虑 。
那么答案就是在所有前缀为 且长度为 的 串中【拆分点的个数】-【拆分点个数为 的个数】。
计算拆分点的个数和
考虑 的拆分点 ,只需要判断 是否是 的前缀即可。
对于 的拆分点 ,后面一半可以任选 的前缀,那么只需算前面有几个满足就行了,也就是计算 在 中出现几次,这个问题等会说。
计算拆分点个数为 的数量
考虑枚举拆分点之间的距离 ,那么这种情况下,要求 的两个拆分点 满足 。
- 若 ,那么只需暴力枚举 ,判断是否合法即可。
- 若 ,类似地,等价于计算 在 中的出现次数。
剩下的问题,就是计算 在 中的出现次数了,这个问题见 QOJ P6842,这题还是加强的。
大概思路就是要建出 的失配树。
那么从 开始往后匹配 等价于 往后匹配 ,然后再匹配 ,倍增计算每个点走过的次数即可。
总时间复杂度 ,可以做到 。
题外话,上面的 Lemma.2 也说明了答案不会超过 ,所以似乎 显得有点多余。
代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
#define all(a) (a).begin(),(a).end()
#ifdef DEBUG
template<class T>
ostream& operator << (ostream &out,vector<T> a){
out<<'[';
for(T x:a)out<<x<<',';
return out<<']';
}
template<class T>
vector<T> ary(T *a,int l,int r){
return vector<T>{a+l,a+1+r};
}
template<class T>
void debug(T x){
cerr<<x<<endl;
}
template<class T,class...S>
void debug(T x,S...y){
cerr<<x<<' ',debug(y...);
}
#else
#define debug(...) void()
#endif
const int N=1e2+10,K=60,mod=1e9+9;
int T,n;
ll k;
char a[N],b[N];
int pre[N][2],suf[N][2];
int ch[N][2],fail[N];
void getfail(){
for(int i=1;i<=n;i++)b[n+1-i]=a[i];
b[n+1]=0;
for(int i=1;i<=n;i++)ch[i-1][b[i]&1]=i;
for(int i=1;i<=n;i++){
for(int c:{0,1}){
int &v=ch[i][c],x=ch[fail[i]][c];
if(v)fail[v]=x;
else v=x;
}
}
}
int to[K][N][2];
void init(){
for(int i=0;i<=n;i++){
for(int c:{0,1}){
to[0][i][c]=ch[i][c];
}
}
for(int i=1;i<K;i++){
for(int u=0;u<=n;u++){
for(int c:{0,1}){
int t=to[i-1][u][c];
to[i][u][c]=to[i-1][t][!c];
}
}
}
}
ll calc(ll l,ll r){
// int ans=0;
// for(int i=0,u=0;i<r;i++){
// u=ch[u][__builtin_parity(i)];
// ans+=i>=l-1&&u==n;
// }
// for(int i=0,u=0;i<r;i++){
// u=ch[u][!__builtin_parity(i)];
// ans+=i>=l-1&&u==n;
// }
// debug(l,r,b+1,ans);
// return ans;
ll las=l;
r++;
static ll cnt[K][N][2];
memset(cnt,0,sizeof cnt);
int u[2]={0,0};
for(int i=K-1,c=0;i>=0;i--){
if(l>>i&1){
for(int x:{0,1}){
u[x]=to[i][u[x]][c^x];
}
c^=1;
}
}
for(int i=0;i<K;i++){
if(l+(1ll<<i)<=r&&(l>>i&1)){
int c=__builtin_parityll(l);
for(int x:{0,1}){
cnt[i][u[x]][c^x]++,u[x]=to[i][u[x]][c^x];
}
l+=1ll<<i;
}
}
for(int i=K-1;i>=0;i--){
if(l+(1ll<<i)<=r&&(~l>>i&1)){
int c=__builtin_parityll(l);
for(int x:{0,1}){
cnt[i][u[x]][c^x]++,u[x]=to[i][u[x]][c^x];
}
l+=1ll<<i;
}
}
for(int i=K-1;i>=1;i--){
for(int u=0;u<=n;u++){
for(int c:{0,1}){
int t=to[i-1][u][c];
cnt[i-1][u][c]+=cnt[i][u][c];
cnt[i-1][t][!c]+=cnt[i][u][c];
}
}
}
// debug(las,r-1,b+1,cnt[0][n][0]+cnt[0][n][1]);
return cnt[0][n][0]+cnt[0][n][1];
}
void get(){
scanf("%s%lld",a+1,&k),n=strlen(a+1);
if(n==1&&!k)return puts("1"),void();
for(int i=1;i<=n;i++){
for(int c:{0,1}){
pre[i][c]=1;
for(int j=0;j<i;j++){
pre[i][c]&=__builtin_parity(j)^c==a[i-j]-'0';
}
suf[i][c]=1;
for(int j=0;j<=n-i;j++){
suf[i][c]&=__builtin_parity(j)^c==a[i+j]-'0';
}
}
}
getfail(),init();
ll ans=0;
for(int i=1;i<n;i++){
ans+=(pre[i][0]+pre[i][1])*(suf[i+1][0]+suf[i+1][1]);
// debug(i,pre[i][0]+pre[i][1],suf[i+1][0]+suf[i+1][1],ans);
}
ans+=calc(0,n+k-1)*2;
// debug(ans);
for(ll len=1;len+2<=n+k;len<<=1){
ll l=max(len+1,n+k-len),r=min(len+len,n+k-1ll);
if(l>r)continue;
// debug(n+k,len,l,r);
ans-=calc(l,r);
for(ll i=l;i<n;i++){
ans-=(pre[i][0]+pre[i][1])*(suf[i-len+1][0]+suf[i-len+1][1]);
}
}
printf("%lld\n",ans%mod);
}
void clr(){
for(int i=0;i<=n;i++){
fail[i]=ch[i][0]=ch[i][1]=0;
}
}
int main(){
freopen(".in","r",stdin);
// freopen(".out","w",stdout);
for(scanf("%d",&T);T--;clr())get();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!