二分递归求等比数列前n项和

2019河北省大学生程序设计竞赛(重现赛)B
我们假设有一个等比数列: a[n] = q ^ n
那么 S[n] = q ^ 1 + q ^ 2 + q ^ 3 …… + q ^ n
正常情况我们有 S[n] = q * (1 - q ^ n) / (1 - q)
若我们需要求的是 S[n] % mod 且 (1 - q)与mod不互质,那么上面的公式便会有问题(逆元无法确定)
这时候我们发现:
如果n为奇数: S[n] = S[n / 2] + a[(n - 1) / 2 + 1]×S[n / 2] + a[(n - 1) / 2 + 1]
如果n为偶数: S[n] = S[n / 2] + a[n / 2]×S[n / 2]

证明

如果n为偶数,我们可以将n分成两个部分,S[n] = S[n / 2] + 后一部分
然后我们发现后一部分其实可以又前一部分乘一个数得来,这个数为a[n / 2 + 1] / a[1],即为q ^ (n / 2)
如果n为奇数,我们可以类比偶数情况,在两部分间加一个数,这样第一部分和第三部分同偶数情况处理,在额外加上中间的数

代码

/*
二分求等比数列前n项和(首项等于公比)
(1)当n为偶数,可将n分为前后两个部分,后部分可以看成前部分每项乘以q^k,这个k即为n/2
(2)当n为奇数,同理偶数,可看成三个部分,前后部分与偶数类似,中间部分是一个数,即为a[(n - 1) / 2 + 1],前后部分之间需乘以q^((n - 1) / 2 + 1) 
 */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll n, q, p;
ll q_pow(ll a, ll b)
{
    ll c = 1;
    while(b)
    {
        if(b & 1)
            c = (c * a) % p;
        a = (a * a) % p;
        b >>= 1;
    }
    return c;
}
ll dfs(ll x)
{
    if(x == 1)
        return q;
    ll dd = dfs(x / 2);
    if(x & 1)//(1 + a[n / 2 + 1]) * S[n / 2] + a[n / 2 + 1]
    {
        ll zz = q_pow(q, x / 2 + 1);
        dd = (dd + dd * zz % p) % p;
        dd = (dd + zz) % p;
    }
    else //(1 + a[n / 2]) * S[n / 2]
    {
        ll zz = q_pow(q, x / 2);
        dd = (dd + dd * zz % p) % p;
    }
    return dd;
}
int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin >> t;
    while(t --)
    {
        cin >> q >> n >> p;
        cout << dfs(n) << endl;
    }
}
全部评论

相关推荐

牛舌:如果我不想去,不管对方给了多少,我一般都会说你们给得太低了。这样他们就会给下一个offer的人更高的薪资了。
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务