「WC2016」论战捆竹竿

「WC2016」论战捆竹竿

给定一个字符串 $s$,假设其 border 集合为 $S$,则每次你可以在 $s$ 后面接上一个长度为 $|s| - x$ 的字符串,其中 $x \in S$。问在总长度 $\leq w$ 的情况下有多少种可能的本质不同的长度。

$n \leq 5 \times 10^5,\ w \leq 10^{18}$。

题解

做法和哥哥们的好像不大一样,不过本质应该差不多,但还是厚颜无耻的来水一篇。

border 的贡献是若干端等差数列,不妨设其中一段为 $kx + b$,其中 $x \in [0,l]$,考虑其产生的贡献整理后可以理解为三种:

  • 长度为 $b$ 的贡献,可以选择 $\inf$ 次;
  • 长度为 $lk + b$ 的贡献,可以选择 $\inf$ 次;
  • 长度为 $(0…l)k + b$ 的贡献,可以选择 $1$ 次。

考虑前两种贡献,就是朴素的同余最短路问题。考虑先计算出他们的 dis 数组,再转移上第三类贡献。

对于每一种等差数列分开处理,分模 $k$ 的余数讨论,容易发现可以直接用单调队列维护下转移。

同余最短路跑 spfa 是线性的,可以直接用(。

时间复杂度 $O(n \log w)$。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include<bits/stdc++.h>
template<class T> inline void read(T &x){
x=0; register char c=getchar(); register bool f=0;
while(!isdigit(c))f^=c=='-',c=getchar();
while(isdigit(c))x=x*10+c-'0',c=getchar(); if(f)x=-x;
}
const int N=5e5+10;
int T,n,fl,mod;
char s[N];
long long w,ans,t[N],dis[2][N];
std::vector<int> key;
struct atom{
int b,k,l;
};
std::vector<atom> res;
namespace priority_queue{
int nod;
struct node{
int pre,nxt,i;
long long x;
}e[N<<1];
inline void init(){
nod=0;
}
struct set{
int lim,hed,til;
inline void reset(int l){
hed=til=0,lim=l;
}
inline void pop_front(){
if(hed==til)hed=til=0;
else{
e[e[til].pre].nxt=0;
til=e[til].pre;
}
}
inline void pop_back(){
if(hed==til)hed=til=0;
else{
e[e[hed].nxt].pre=0;
hed=e[hed].nxt;
}
}
inline void push_back(int i,long long x){
e[++nod].i=i,e[nod].x=x;
if(!hed){
hed=til=nod;
e[nod].pre=e[nod].nxt=0;
}else{
e[nod].nxt=hed,e[hed].pre=nod;
hed=nod;
}
}
inline void insert(int i,long long x){
while(hed&&e[hed].x>x)pop_back();
push_back(i,x);
while(e[til].i<i-lim)pop_front();
}
inline long long query(){return e[til].x;}
};
}
priority_queue::set set[N];
namespace border_finder{
const int b=131131,p1=998244353,p2=1e9+7;
int f[N],g[N],pf[N],pg[N];
std::vector<int> vet;
inline int query(int l,int r,int *a,int *pa,int p){
int res=(a[r]-(long long)a[l-1]*pa[r-l+1])%p;
return res<0?res+p:res;
}
void solve(int n){
pf[0]=pg[0]=1,vet.clear(),res.clear();
for(int i=1;i<=n;i++){
pf[i]=(long long)pf[i-1]*b%p1;
pg[i]=(long long)pg[i-1]*b%p2;
f[i]=((long long)f[i-1]*b+s[i]-'a')%p1;
g[i]=((long long)g[i-1]*b+s[i]-'a')%p2;
}
for(int i=n-1;i>=1;i--)
if(query(1,i,f,pf,p1)==query(n-i+1,n,f,pf,p1)&&query(1,i,g,pg,p2)==query(n-i+1,n,g,pg,p2))
vet.push_back(n-i);
vet.push_back(n);
int first=vet[0],delta=0,cnt=0;
for(int i=1;i<vet.size();i++)if(!delta){
delta=vet[i]-vet[i-1],cnt=1;
}else{
if(vet[i]-vet[i-1]==delta)++cnt;
else{
res.push_back((atom){first,delta,cnt});
first=vet[i],delta=0,cnt=0;
}
}
res.push_back((atom){first,delta,cnt});
}
}
void spfa(std::vector<int> &key,long long *dis){
static int l,r,q[N<<3]; static bool inq[N];
mod=*std::max_element(key.begin(),key.end());
memset(dis,63,mod<<3);
q[l=r=1]=dis[0]=0;
while(l<=r){
int u=q[l++]; inq[u]=0;
for(int w:key){
int c=(u+w)/mod; int v=u+w-c*mod;
if(dis[u]+c<dis[v]){
dis[v]=dis[u]+c;
if(!inq[v])inq[v]=1,q[++r]=v;
}
}
}
}
void trans(long long *f,long long *g,const atom &it){
if(!it.l){
for(int i=0;i<mod;i++){
g[i]=std::min(f[i],f[(i-it.b+mod)%mod]);
}
return;
}
memset(t,63,mod<<3),priority_queue::init();
for(int i=0;i<it.k;i++)set[i].reset(it.l);
for(int i=-mod;i<mod;i++){
auto &set=::set[(i+mod)%it.k];
set.insert((i+mod)/it.k,i<0?f[i+mod]+1:f[i]);
if(i>=0)t[i]=set.query();
}
for(int i=0;i<mod;i++){
g[i]=std::min(f[i],i<it.b?t[i-it.b+mod]+1:t[i-it.b]);
}
}
int main(){
for(read(T);T--;ans=0,key.clear()){
scanf("%d%lld%s",&n,&w,s+1);
if(w<n){puts("0"); continue;}
border_finder::solve(n);
for(auto x:res){
key.push_back(x.b);
if(x.l)key.push_back(x.b+x.k*x.l);
}
spfa(key,dis[fl]);
for(auto x:res){
trans(dis[fl],dis[fl^1],x);
fl^=1;
}
for(int i=0;i<mod;i++)if(dis[fl][i]!=4557430888798830399){
ans+=std::max(0ll,(w-n-i)/mod+1-dis[fl][i]);
}
printf("%lld\n",ans);
}
}