题解 P5126 【鬼故事】
NaCly_Fish · · 题解
update:由于这题标算被爆了多次,就详细详细讲一下各做法吧(合订本既视感)。
出题人做法:
构造一个矩阵来做快速幂,时间复杂度
本人第一次做法:
既然这是线性递推,那我们可以用 BM 算法 找出这个递推式,利用优化的线性递推算法,时间复杂度为
提交记录
能不能给力一点啊?
第二次看这题的时候,已经学习了 这题 EI 的特征根性质优化做法(orz EI!),下面做详细讲解。
设
则
分类讨论一下
再用
设这两个多项式的
还有一点常数优化,就是
现在考虑如何求出那两个多项式的系数,只考虑后面那个乘积,是这样一个形式:
(虽然原式中是
这个式子可以倍增处理,以
提取系数即得
从上边界
当然,你会发现计算快速幂时只有两次指数特别大,而
显然是有循环节的,所以可以直接把指数对
总时间复杂度为
参考代码(稍微改改参数就能直接通过加强版):
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 20003
#define ll long long
#define p 1000000007
using namespace std;
inline int add(const int& x,const int& y){ return x+y>=p?x+y-p:x+y; }
inline int dec(const int& x,const int& y){ return x<y?x-y+p:x-y; }
inline int intpow(int a,int t){
int res = 1;
while(t){
if(t&1) res = (ll)res*a%p;
a = (ll)a*a%p;
t >>= 1;
}
return res;
}
struct complex{
int r,i;
inline complex(int _r=0,int _i=0):r(_r),i(_i){}
};
inline complex operator + (const complex& lhs,const complex& rhs){ return complex(add(lhs.r,rhs.r),add(lhs.i,rhs.i)); }
inline complex operator - (const complex& lhs,const complex& rhs){ return complex(dec(lhs.r,rhs.r),dec(lhs.i,rhs.i)); }
inline complex operator * (const complex& lhs,const complex& rhs){ return complex(((ll)lhs.r*rhs.r+5ll*lhs.i*rhs.i)%p,((ll)lhs.r*rhs.i+(ll)lhs.i*rhs.r)%p); }
inline complex operator - (const complex& x){ return complex(dec(0,x.r),dec(0,x.i)); }
inline complex operator * (const complex& lhs,const int& rhs){ return complex((ll)lhs.r*rhs%p,(ll)lhs.i*rhs%p); }
const complex one = complex(1,0);
inline complex power(complex a,ll t){
complex res = complex(1,0);
while(t){
if(t&1) res = res*a;
a = a*a;
t >>= 1;
}
return res;
}
inline complex inverse(complex x){
int d = ((ll)x.r*x.r-5ll*x.i*x.i%p+p)%p;
return complex(x.r,p-x.i)*intpow(d,p-2);
}
complex pre[N>>1];
void product(int k,complex q,complex *t){
t[k] = 1;
static complex pw[N];
pw[0] = pre[0] = one;
for(int i=1;i<=k;++i) pw[i] = pw[i-1]*q;
for(int i=1;i<=k;++i) pre[i] = pre[i-1]*(pw[k]-pw[i-1]);
complex Inv = inverse(pre[k]),suf = one;
for(int i=k;i;--i){
t[i-1] = pw[k-1]*(pw[i]-one)*(Inv*pre[i-1]*suf)*t[i];
suf = suf*(pw[k]-pw[i-1]);
}
}
char L[N],R[N];
int k;
complex f[N],g[N],inv1[N>>1],inv2[N>>1],suf[N>>1];
const int ninv2 = p-(p-1)/2;
const ll md = 4ll*p*(p+1);
const __int128_t ten = 10;
int main(){
scanf("%d",&k);
scanf("%s%s",L,R);
int lenl = strlen(L),lenr = strlen(R),lmdp = 0,rmdp = 0;
ll pwl = 0,pwr = 0;
for(int i=0;i<lenl;++i) lmdp = (lmdp*10ll+L[i]-'0')%p,pwl = (pwl*ten+L[i]-'0')%md;
for(int i=0;i<lenr;++i) rmdp = (rmdp*10ll+R[i]-'0')%p,pwr = (pwr*ten+R[i]-'0')%md;
reverse(L,L+lenl),reverse(R,R+lenr);
int inv5 = intpow(5,p-2);
complex alpha = complex(ninv2,ninv2),A = complex(0,inv5);
complex iva = inverse(alpha),q,beta,ans = 0,a2 = alpha*alpha;
q = power(iva,k),beta = -iva*iva;
product(k,beta,g);
for(int i=0;i<=k;++i) f[i] = (k-i)&1?-g[i]:g[i];
complex pal = power(alpha,pwl),par = power(alpha,pwr+1);
complex pa2l = pal*pal,pa2r = par*par,pql = power(inverse(pal),k),pqr = power(inverse(par),k);
complex pali = one,pari = one,qs,nqs,npql = (L[0]&1)?-pql:pql,npqr = (R[0]&1)?pqr:-pqr;
inv1[0] = q,inv2[0] = q+one;
for(int i=1;i<=k;++i){
inv1[i] = inv1[i-1]*a2;
inv2[i] = inv1[i]+one;
}
for(int i=0;i<=k;++i) inv1[i] = inv1[i]-one;
if(!(k&1)) inv1[k>>1] = one;
pre[0] = suf[k+1] = one;
inv1[0] = inverse(inv1[0]);
for(int i=1;i<=k;++i) pre[i] = pre[i-1]*inv1[i];
for(int i=k;i;--i) suf[i] = suf[i+1]*inv1[i];
complex mul = inverse(pre[k]);
for(int i=1;i<=k;++i) inv1[i] = pre[i-1]*suf[i+1]*mul;
inv2[0] = inverse(inv2[0]);
for(int i=1;i<=k;++i) pre[i] = pre[i-1]*inv2[i];
for(int i=k;i;--i) suf[i] = suf[i+1]*inv2[i];
mul = inverse(pre[k]);
for(int i=1;i<=k;++i) inv2[i] = pre[i-1]*suf[i+1]*mul;
for(int i=0;i<=k;++i){
if(q.r==1&&q.i==0) qs = dec(rmdp+1,lmdp);
else qs = (pqr*pari - pql*pali)*inv1[i];
nqs = (npqr*pari - npql*pali)*inv2[i];
ans = ans + (f[i]+g[i])*qs - (f[i]-g[i])*nqs;
pali = pali*pa2l,pari = pari*pa2r;
q = q*a2;
}
ans = ans*power(A,k)*power(alpha,k*(k-1)>>1)*ninv2;
printf("%d",ans.r);
return 0;
}
关于「能不能再给力一点啊?」这个问题,我对 q-整式递推 还不甚了解,就不妄议了。如果哪天复杂度再有优化,还会回来补的(