Border Theory 学习笔记
若字符串
若字符串
定理 0:若
证明:由于
定理 1(弱周期定理,Weak Periodicity Lemma,WPL):若
证明:不妨设
证毕。
还有强周期定理:若
推论:将上述前缀
证明:gcd 性质易证。
定理 2:对于所有
证明:考虑两个 Border
定理 3:将所有 Border 按其二进制最高位进行分段,每一段内的 Border 都构成一个等差数列。
证明:设
因此,我们可以将
例题:[WC2016] 论战捆竹竿
题意:给定一个串
题解:对于若干种值线性组合相关问题,一般考虑同余最短路。具体来说,我们直接在模最小数
考虑对于一个等差数列,应该如何跑同余最短路。设此等差数列为
尝试将不同等差数列跑出的结果进行合并。注意到最大的问题在于模数会发生改变。设之前的模数为
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int INF=1e18;
char s[500005];
int nxt[500005];
int lg[1000005];
int dis[500005],tmp[500005];
vector<int>border[25];
void kmp(int n){
for(int i=2;i<=n;i++){
int p=nxt[i-1];
while(p&&s[p+1]!=s[i]) p=nxt[p];
if(s[p+1]==s[i]) p++;
nxt[i]=p;
}
int x=nxt[n];
while(x){
border[lg[x]].push_back(n-x);
x=nxt[x];
}
border[0].push_back(n);
}
int pos[500005];
int q[500005],ft=1,ed=0;
void upd(int bg,int x1,int d,int L,int m){
int now=(bg+d)%m,mn=dis[bg],mnpos=bg;
while(now!=bg){
if(dis[now]<mn) mn=dis[now],mnpos=now;
now=(now+d)%m;
}
int len=1;
now=mnpos;
pos[1]=now;
now=(now+d)%m;
while(now!=pos[1]){
pos[++len]=now;
now=(now+d)%m;
}
ft=1,ed=0;
q[++ed]=1;
for(int i=2;i<=len;i++){
while(ed>=ft&&q[ft]<=i-L) ft++;
if(ed>=ft) dis[pos[i]]=min(dis[pos[i]],dis[pos[q[ft]]]+d*(i-q[ft])+x1);
while(ed>=ft&&dis[pos[i]]+i*d<=dis[pos[q[ed]]]+q[ed]*d) ed--;
q[++ed]=i;
}
}
void tyzdl(int x1,int d,int L,int p){
int g=__gcd(p,d);
for(int i=0;i<g;i++) upd(i,x1,d,L,p);
}
signed main(){
lg[1]=0;
for(int i=2;i<=1000000;i++) lg[i]=lg[i>>1]+1;
int t,n,w;
cin>>t;
while(t--){
cin>>n>>w;
w-=n;
scanf("%s",s+1);
if(w<0){
puts("0");
continue;
}
for(int i=0;i<=20;i++) border[i].clear();
for(int i=0;i<=n;i++) dis[i]=INF;
dis[0]=0;
kmp(n);
int pre=-1;
for(int i=0;i<=20;i++){
if(border[i].size()){
int now=border[i][0];
if(pre!=-1){
for(int i=0;i<=n;i++) tmp[i]=INF;
for(int i=0;i<pre;i++){
int ok=dis[i]%now;
tmp[ok]=min(tmp[ok],dis[i]);
}
swap(tmp,dis);
tyzdl(0,pre,2,now);
}
pre=now;
if(border[i].size()>1) tyzdl(border[i][0],border[i][1]-border[i][0],border[i].size(),now);
}
}
int ans=0;
for(int i=0;i<pre;i++) if(dis[i]<=w) ans+=(w-dis[i])/pre+1;
printf("%lld\n",ans);
}
return 0;
}