Educational Codeforces Round 51 (Rated for Div. 2)E. Vasya and Big Integers(二分雜湊+差分)
阿新 • • 發佈:2018-12-11
題意
給出長度小於等於的數字串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);
}