题解 P6435 【「EZEC-1」数列】

传送门

久违地来一波题解,来一个数学角度推式子的方法


【分析】

记第 (k) 次合并后的第 (i) 个数为 (F_{k,i})

根据题意,所求即为 (ans=F_{n-1,n}) ,且有:

(F_{k+1,n}=aF_{k,n}+bF_{k,n+1}+c,F_{0,i}=i)

形式上,我们设 (exists dwedge F_{k+1,i}+d=a(F_{k,i}+d)+b(F_{k,i+1}+d))

则不难换算出 ((a+b-1)d=c)

注意,只是形式上这样设, (d) 可能本身不存在

(G_{k,i}=F_{k,i}+d) 则得到: (G_{k+1,i}=aG_{k,i}+bG_{k,i+1})

考虑 (G) 之间的转移,发现与杨辉三角类似,故考虑每个 (G_{0,i}) 的贡献,很容易得到:

(displaystyle G_{n-1,n}=sum_{i=1}^ndbinom{n-1}{i-1}a^{(n-1)-(i-1)}b^{i-1}G_{0,i})

代回 (G_{k,i}=F_{k,i}+d,F_{0,i}=i,ans=F_{n-1,n}) ,换元 (i=i-1)

(displaystyle ans=sum_{i=0}^{n-1}dbinom{n-1}ia^{(n-1)-i}b^i(i+1+d)-d)

(d) 展开后得到:

(displaystyle ans=sum_{i=0}^{n-1}dbinom{n-1}ia^{(n-1)-i}b^icdot (i+1)+d[sum_{i=0}^{n-1}dbinom{n-1}ia^{(n-1)-i}b^i-1])

后半部分是个很显然的二项式定理,求和得到 ((a+b)^{n-1})

再代回 ((a+b-1)d=c) 得到:(displaystyle ans=sum_{i=0}^{n-1}dbinom{n-1}ia^{(n-1)-i}b^i(i+1)+ccdot {(a+b)^{n-1}-1over (a+b)-1})

接着,我们构造函数,设 (displaystyle f(x)=sum_{i=0}^{n-1}dbinom{n-1}ia^{(n-1)-i}x^{i+1}=x(a+x)^{n-1})

因此 (displaystyle { ext dover ext dx}f(x)=sum_{i=0}^{n-1}dbinom{n-1}ia^{(n-1)-i}x^icdot (i+1)={ ext dover ext dx}[x(a+x)^{n-1}]=(a+x)^{n-1}+xcdot (a+x)^{n-2}cdot (n-1))

整理得到: (displaystyle sum_{i=0}^{n-1}dbinom{n-1}ia^{(n-1)-i}x^icdot (i+1)=(a+x)^{n-2}cdot (a+nx))

所以得到 (displaystyle sum_{i=0}^{n-1}dbinom{n-1}ia^{(n-1)-i}b^icdot (i+1)=(a+b)^{n-2}cdot (a+nb))

最后我们得到: (ans=(a+b)^{n-2}cdot (a+nb)+ccdot {1-(a+b)^{n-1}over 1-(a+b)})


法一:

对于前半部分,快速幂 (O(log n)) 可直接算出;而后半部分,可能不存在 (1-(a+b)) 的逆元,不能直接求解

考虑到其形式上为等比数列求和式,展开为: (1+(a+b)+(a+b)^2+cdots +(a+b)^{n-2})

(sumq(q,n)=1+q+q^2+cdots +q^n)

则取一个数 (m) ,使得 (1+q+q^2+cdots +q^n=(1+q+q^2+cdots +q^m)(1+q^{m+1}+q^{2(m+1)+cdots}+q^{lfloor{n-mover m+1} floorcdot (m+1)})+q^{m+lfloor{n-mover m+1} floorcdot (m+1)+1}(1+q+cdots))

因此 (sumq(q,n)=sumq(q,m)cdot sumq(q^{m+1},lfloor{n-mover m+1} floor)+q^{m+lfloor{n-mover m+1} floorcdot (m+1)+1}cdot sumq(q,n-m-lfloor{n-mover m+1} floorcdot (m+1)-1)) ,可以递归求解

通过估算,大概 (m=sqrt n) 时最优


现分析求 (sumq(q,n)) 的复杂度:

(T(n)approx T(m)+T(m)+T(n-m^2)+log m+log nleq 3T(n^{1over 2})+{3over 2}log n)

(k=log n)(T(k)=3T({kover 2})+{3over 2}k)

再记 (2^a=k)(T(a)=3T(a-1)+{3over 2}cdot 2^a)

解递推方程得到 (T(a)=3^a+o(3^a)=O(3^a))

代回得 (3^a=2^{alog_2 3}=k^{log_2 3}=log^{log_2 3} n)

因此 (T(n)=O(log^{log_2 3} n)approx O(log^{1.6} n))


【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,a,b,c,p,ans=1;
inline __int128 fpow(__int128 a,ll x) { ll ans=1; for(;x;x>>=1,a=a*a%p) if(x&1) ans=ans*a%p; return ans; }
inline __int128 sumq(__int128 q,ll n){
    if(n==0) return 1%p;
    if(n==1) return (q+1)%p;
    ll m=sqrt(n),tmp=(n-m)/(m+1)*(m+1)+m;
    __int128 res=sumq(q,m)*sumq( fpow(q,m+1) , (n-m)/(m+1) )%p;
    if(tmp<n) res+=fpow(q,tmp+1)*sumq(q,n-tmp-1)%p;
    return (res%p+p)%p;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    cin>>n>>a>>b>>c>>p;
    if(n>1) ans=fpow(a+b,n-2)*(a+(__int128)n*b%p)%p+c*sumq(a+b,n-2)%p;
    ans=(ans%p+p)%p;
    cout<<ans;
    cout.flush();
    return 0;
}

法二:

想了想,好像没那么复杂

考虑到 (a,bgeq 1),所以 (a+b>1) 一定成立

因此,前半部分同样快速幂直接跑

后半部分为形如 ({q^n-1over q-1},q>1) 的形式

({q^n-1over q-1}mod p=t)

({q^n-1over q-1}-kp=t)

移项得到 (q^n-kp(q-1)=t(q-1)+1)

因此 (q^nmod p(q-1)=t(q-1)+1)

由于我们所求为 (t) ,因此得出:

(t={q^nmod p(q-1)-1over q-1})

我们直接把 (q) 在模 (p(q-1)) 的意义下跑快速幂,结果直接进行这个处理,就能得到我们索要的结果了

其中,(p(q-1)leq 10^{14}cdot (10^9+10^9)=2 imes 10^{23}) ,要使用 __int128

由于数值过大,甚至对 __int128 写拆位的快速乘都不行,因此只能龟速乘

时间复杂度为 (O(log^2 n)) ,但由于没有递归,运行速率好像反而比法一快


【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,a,b,c,p,ans=1;
inline __int128 fpow(__int128 a,__int128 x,__int128 m) { __int128 ans=1; for(;x;x>>=1,a=a*a%m) if(x&1) ans=ans*a%m; return ans; }
//不用龟速乘的快速幂
inline __int128 fmul(__int128 a,__int128 x,__int128 m) { __int128 ans=0; for(;x;x>>=1,a=(a+a)%m) if(x&1) ans=(a+ans)%m; return ans; }
inline __int128 fpoww(__int128 a,__int128 x,__int128 m) { __int128 ans=1; for(;x;x>>=1,a=fmul(a,a,m)) if(x&1) ans=fmul(a,ans,m); return ans; }
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    cin>>n>>a>>b>>c>>p;
    if(n>1) ans=fpow(a+b,n-2,p)*(a+(__int128)n*b%p)%p+(fpoww(a+b,n-1,(__int128)p*(a+b-1))-1)/(a+b-1)*c%p;
    ans=(ans%p+p)%p;
    cout<<ans;
    cout.flush();
    return 0;
}

虽然必须用龟速乘,但我们可以对龟速乘搞事情:

由于 (127-log_2 (2 imes 10^{23})approx49)

所以我们龟速乘可以48位、48位地算,这样就只要3次计算

(稍微卡了一下常数)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,a,b,c,p,ans=1;
inline ll fpow(__int128 a,ll x,ll m) { ll ans=1; for(;x;x>>=1,a=a*a%m) if(x&1) ans=ans*a%m; return ans; }
//不用龟速乘的快速幂
inline __int128 fmul(__int128 a,__int128 x,__int128 m) { __int128 ans=0; for(;x;x>>=48,a=(a<<48)%m) ans=(a*(x&(1ll<<48)-1)+ans)%m; return ans; }
inline __int128 fpoww(__int128 a,__int128 x,__int128 m) { __int128 ans=1; for(;x;x>>=1,a=fmul(a,a,m)) if(x&1) ans=fmul(a,ans,m); return ans; }
int main(){
    cin>>n>>a>>b>>c>>p;
    if(n>1) ans=fpow(a+b,n-2,p)*(a+(__int128)n*b%p)%p + (fpoww(a+b,n-1,(__int128)p*(a+b-1))-1)/(a+b-1)*c%p +p;
    cout<<ans%p;
    return 0;
}

目前好像是榜一

开了 o2

不开 o2

原文地址:https://www.cnblogs.com/JustinRochester/p/14348490.html