版权声明:转载需要注明哦QwQ,地址:http://blog.csdn.net/effervescence。 https://blog.csdn.net/Effervescence/article/details/82800616
题意
给出长度小于等于 的数字串a,l,r,求把串a拆分后,每段数字大小都是 并且 的方案有多少种。
分析
首先我们可以发现一个很显然的结论,即如果从第i位开始截成一段,那么这一段的可行的右端点一定是一个连续的区间。那么我们可以想到一个 的DP,就是对于串a,预处理出两个数组la,ra,表示如果从第i位开始切成一段,那么这一段的结尾可以落在 中,令 表示前i位划分合法的方案数,那么对于每一位从前往后,这一位对后面的贡献就是 ,最后答案就是 了。
考虑如何加速找到数组la,ra的这个过程,我们知道我们比较两个长度相等的大整数的时候,是从高位开始逐位比较,直到比到不一样的一位或者当前位置超过了某一个整数的位数,根据这个想法,我们可以考虑对于串a,对于每一位求出从第i位开始对串l和串r的lcp是多少,然后我们就可以比较出可行的范围了。求lcp的这个过程我们可以用预处理 每一位询问 的Exkmp或SA,或者使用二分哈希,每一位查询是 的,也是可以通过的。由于 对后面的位置的答案是连续一段的,所以我们就可以通过差分搞定这个区间加,这样的话这题就做完了,我用了二分哈希,然后由于是Edu,怕被卡哈希,就用了三模哈希,常数略微大了点,总复杂度是 的,700ms AC。
Code
#pragma GCC optimize("3","Ofast","inline")
#include<bits/stdc++.h>
#define _ 0
using namespace std;
template<class T>inline void read(T &x) {
x=0;T f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-48,ch=getchar();
x*=f;
}
inline int Add(const int &x,const int &y,const int &mod) {
int res=x+y;
return res>=mod?res-mod:res;
}
inline int Sub(const int &x,const int &y,const int &mod) {
int res=x-y;
return res<0?res+mod:res;
}
inline int Mul(const int &x,const int &y,const int &mod) {
return 1ll*x*y%mod;
}
inline int Pow(int x,int y,const int &mod) {
int res=1;
while(y) {
if(y&1)
res=Mul(res,x,mod);
x=Mul(x,x,mod);
y>>=1;
}
return res;
}
const int base=10,M=998244353;
char a[1000005],l[1000005],r[1000005];
int la,ll,lr;
struct Hash {
int numa[1000005],numl[1000005],numr[1000005],mod,inv[1000005],fac[1000005];
inline void init(const int &m) {
mod=m;
int P=1e6;
fac[0]=1;
for(int i=1;i<=P;++i)
fac[i]=Mul(fac[i-1],base,mod);
inv[P]=Pow(fac[P],mod-2,mod);
for(int i=P-1;~i;--i)
inv[i]=Mul(inv[i+1],base,mod);
for(int i=la;i;--i)
numa[i]=Add(numa[i+1],Mul(fac[la-i],a[i]-'0',mod),mod);
for(int i=ll;i;--i)
numl[i]=Add(numl[i+1],Mul(fac[ll-i],l[i]-'0',mod),mod);
for(int i=lr;i;--i)
numr[i]=Add(numr[i+1],Mul(fac[lr-i],r[i]-'0',mod),mod);
// cout<<"Now mod = "<<mod<<endl;
// for(int i=1;i<=la;++i)
// cout<<numa[i]<<" ";
// cout<<endl;
// for(int i=0;i<10;++i)
// cout<<inv[i]<<" ";
// cout<<endl;
}
inline int calca(const int &l,const int &r) {
return Mul(Sub(numa[l],numa[r+1],mod),inv[la-r],mod);
}
inline int calcl(const int &l,const int &r) {
return Mul(Sub(numl[l],numl[r+1],mod),inv[ll-r],mod);
}
inline int calcr(const int &l,const int &r) {
return Mul(Sub(numr[l],numr[r+1],mod),inv[lr-r],mod);
}
}H1,H2,H3;
inline int solvel(const int &p,const int &l) {
return H1.calca(p,p+l-1)==H1.calcl(1,l)&&H2.calca(p,p+l-1)==H2.calcl(1,l)&&H3.calca(p,p+l-1)==H3.calcl(1,l);
}
inline int solver(const int &p,const int &l) {
return H1.calca(p,p+l-1)==H1.calcr(1,l)&&H2.calca(p,p+l-1)==H2.calcr(1,l)&&H3.calca(p,p+l-1)==H3.calcr(1,l);
}
inline int lcpl(const int &p) {
int l=0,r=min(ll,la-p+1),mid,ans=0;
while(l<=r) {
int mid=(l+r)>>1;
if(solvel(p,mid))
l=mid+1,ans=mid;
else
r=mid-1;
}
return ans;
}
inline int lcpr(const int &p) {
int l=0,r=min(lr,la-p+1),mid,ans=0;
while(l<=r) {
int mid=(l+r)>>1;
if(solver(p,mid))
l=mid+1,ans=mid;
else
r=mid-1;
}
return ans;
}
int f[1000005],g[1000005];
int main() {
scanf("%s%s%s",a+1,l+1,r+1);
la=strlen(a+1),ll=strlen(l+1),lr=strlen(r+1);
H1.init(998244353),H2.init(993244853),H3.init(1000000007);
// cout<<H1.calca(1,2)<<" "<<H2.calca(1,2)<<" "<<H3.calca(1,2)<<" "<<H1.calcr(1,1)<<endl;
g[0]=1;
for(int i=0;i<=la;++i) {
int pl=i+1,pr=i+1;
if(i)
g[i]=f[i]=Add(f[i],f[i-1],M);
if(i==la)
break;
if(a[i+1]=='0') {
if(l[1]!='0')
pr=pl-1;
}
else {
int j=i+1,p=lcpl(j);
// cout<<"Pos :"<<j<<" "<<p<<endl;
if(p==ll)
pl=j+ll-1;
else if(a[j+p]>l[p+1])
pl=j+ll-1;
else if(a[j+p]<l[p+1])
pl=j+ll;
p=lcpr(j);
// cout<<"Pos :"<<j<<" "<<p<<endl;
if(p==lr)
pr=j+lr-1;
else if(a[j+p]>r[p+1])
pr=j+lr-2;
else if(a[j+p]<r[p+1])
pr=j+lr-1;
}
// cout<<"Now at:"<<i<<" Value :"<<f[i]<<" Trans :"<<pl<<" -> "<<pr<<endl;
f[pl]=Add(f[pl],g[i],M),f[pr+1]=Sub(f[pr+1],g[i],M);
// for(int j=0;j<=la;++j)
// cout<<f[j]<<" ";
// cout<<endl;
// for(int j=0;j<=la;++j)
// cout<<g[j]<<" ";
// cout<<endl;
}
printf("%d\n",g[la]);
return ~~(0^_^0);
}