Light OJ 1054 - Efficient Pseudo Code

Sometimes it's quite useful to write pseudo codes for problems. Actually you can write the necessary steps to solve a particular problem. In this problem you are given a pseudo code to solve a problem and you have to implement the pseudo code efficiently. Simple! Isn't it? :)

pseudo code

{

    take two integers n and m

    let p = n ^ m (n to the power m)

    let sum = summation of all the divisors of p

    let result = sum MODULO 1000,000,007

}

Now given n and m you have to find the desired result from the pseudo code. For example if n = 12 and m = 2. Then if we follow the pseudo code, we get

pseudo code

{

    take two integers n and m

    so, n = 12 and m = 2

    let p = n ^ m (n to the power m)

    so, p = 144

    let sum = summation of all the divisors of p

    so, sum = 403, since the divisors of p are 1, 2, 3, 4, 6, 8, 9, 12, 16, 18, 24, 36, 48, 72, 144

    let result = sum MODULO 1000,000,007

    so, result = 403

}
Input

Input starts with an integer T (≤ 5000), denoting the number of test cases.

Each test case will contain two integers, n (1 ≤ n) and m (0 ≤ m). Each of n and m will be fit into a 32 bit signed integer.

Output

For each case of input you have to print the case number and the result according to the pseudo code.

Sample Input
3
12 2
12 1
36 2
Output for Sample Input
Case 1: 403
Case 2: 28
Case 3: 3751

题目大意就是说计算n的m次方的所有因子和,由于n和m的范围是在int范围内,所以不能暴力求解。

首先,将n分解为质数的方的乘积。

例如: \(n = p_{1}^{x_1} * p_{2}^{x_2} * p_{3}^{x_3} * ... * p_{k}^{x_k}\) 其中\(p_x\)为质数。

所以 \(n^m = p_{1}^{x_{1}*m} * p_{2}^{x_{2}*m} * p_{3}^{x_{3}*m} * ... * p_{k}^{x_{k}*m}\).

所以\(n^m\)的所有因子和为:

\(sum = (p_{1}^0 + p_{1}^1 + p_{1}^2 + p_{1}^3 + ... + p_{1}^{x_{1}*m})*(p_{2}^0 + p_{2}^1 + p_{2}^2 + p_{2}^3 + ... + p_{2}^{x_{2}*m})*(p_{3}^0 + p_{3}^1 + p_{3}^2 + p_{3}^3 + ... + p_{3}^{x_{3}*m})* ... *(p_{k}^0 + p_{k}^1 + p_{k}^2 + p_{k}^3 + ... + p_{k}^{x_{k}*m})\)

到达此步后我们就可以用两种方法直接计算出\((p_{k}^0 + p_{k}^1 + p_{k}^2 + p_{k}^3 + ... + p_{k}^{x_{k}*m})\)
一项的解

解法一

通过等比数列前n项和公式,在通过逆元,即可算出这一项的和

代码如下:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
vector<ll> prim;
const ll mod = 1000000007;

ll quick_pow(ll x, ll n)
{
    ll res = 1;
    while(n > 0)
    {
        if(n & 1)
            res = (res * x) % mod;
        x = (x * x) % mod;
        n >>= 1;
    }
    return res;
}

void get_prim()
{
    ll arr[100000];
    memset(arr, false, sizeof(arr));
    for(ll i=2; i<100000; ++ i)
    {
        if(arr[i] == false)
        {
            prim.push_back(i);
            for(ll j=i*i; j<100000; j+=i)
                arr[i] = true;
        }
    }
}

void extgcd(ll a, ll b, ll &x, ll &y)
{
    if(b)
    {
        extgcd(b, a % b, y, x);
        y -= (a / b) * x;
    }
    else
    {
        x = 1, y = 0;
    }
}

vector<pair<ll, ll> >res;

void solve(int cases)
{
    ll n, m;
    cin >> n >> m;
    res.clear();
    for(int i=0; prim[i]*prim[i] <= n; ++ i)
    {
        ll s = 0;
        while(n % prim[i] == 0)
            n /= prim[i], s ++;
        if(s != 0)
            res.push_back(make_pair(prim[i], s*m));
    }
    if(n != 1)
        res.push_back(make_pair(n, m));

    ll ans = 1;
    for(int i=0; i<res.size(); ++ i)
    {
        ll x, y;
        extgcd(res[i].first-1, mod, x, y);
        while(x < 0)
            x += mod;
        ans = (ans * (x * ((quick_pow(res[i].first, res[i].second+1)-1 + mod)%mod) % mod)) % mod;
    }
    cout << "Case " << cases << ": ";
    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    //freopen("out.txt", "w", stdout);
    get_prim();

    int t;
    cin >> t;
    for(int i=1; i<=t; ++ i)
        solve(i);
    return 0;
}

解法二

折半递归计算

对于\(p_{k}^0 + p_{k}^1 + p_{k}^2 + p_{k}^3 + ... + p_{k}^{x_{k}*m}\)

  • 如果\(x_{k}*m\)为奇数。则这一项可以写成这样\(p_{k}^0 + p_{k}^1 + p_{k}^2 + p_{k}^3 + ... + p_{k}^{x_{k}*m/2-1} + p_{k}^{x_{k}*m/2}*(p_{k}^0 + p_{k}^1 + p_{k}^2 + p_{k}^3 + ... + p_{k}^{x_{k}*m/2-1})\),然后合并同类项可得:\((p_{k}^{x_{k}*m}+1)*(p_{k}^0 + p_{k}^1 + p_{k}^2 + p_{k}^3 + ... + p_{k}^{x_{k}*m/2-1})\),然后依次递归下去
  • 如果\(x_{k}*m\)为偶数。则可写成\(p_{k}^0 + p_{k}^1 + p_{k}^2 + p_{k}^3 + ... + p_{k}^{x_{k}*m/2-1} + p_{k}^{x_{k}*m/2} + p_{k}^{x_{k}*m/2+1}*(p_{k}^0 + p_{k}^1 + p_{k}^2 + p_{k}^3 + ... + p_{k}^{x_{k}*m/2-1})\),然后合并同类项可得:\(p_{k}^{x_{k}*m/2} + (p_{k}^{x_{k}*m/2+1}+1)*(p_{k}^0 + p_{k}^1 + p_{k}^2 + p_{k}^3 + ... + p_{k}^{x_{k}*m/2-1})\),然后依次递归下去

递归完毕后就可以求得这一项的和。

代码如下:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
vector<ll> prim;
const ll mod = 1000000007;

ll quick_pow(ll x, ll n)
{
    ll res = 1;
    while(n > 0)
    {
        if(n & 1)
            res = (res * x) % mod;
        x = (x * x) % mod;
        n >>= 1;
    }
    return res;
}

void get_prim()
{
    ll arr[100000];
    memset(arr, false, sizeof(arr));
    for(ll i=2; i<100000; ++ i)
    {
        if(arr[i] == false)
        {
            prim.push_back(i);
            for(ll j=i*i; j<100000; j+=i)
                arr[i] = true;
        }
    }
}

ll cou(ll x, ll n)
{
    if(n == 0)
        return 1;

    if(n & 1)
        return ((quick_pow(x, (n+1)/2) + 1) * cou(x, n/2)) % mod;
    else
        return (quick_pow(x, n/2) + (quick_pow(x, n/2+1) + 1) * cou(x, (n-2)/2)) % mod;
}

vector<pair<ll, ll> >res;

void solve(int cases)
{
    ll n, m;
    cin >> n >> m;
    res.clear();
    for(int i=0; prim[i]*prim[i] <= n; ++ i)
    {
        ll s = 0;
        while(n % prim[i] == 0)
            n /= prim[i], s ++;
        if(s != 0)
            res.push_back(make_pair(prim[i], s*m));
    }
    if(n != 1)
        res.push_back(make_pair(n, m));

    ll ans = 1;
    for(int i=0; i<res.size(); ++ i)
        ans = (ans * cou(res[i].first, res[i].second)) % mod;
    cout << "Case " << cases << ": ";
    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    //freopen("out.txt", "w", stdout);
    get_prim();

    int t;
    cin >> t;
    for(int i=1; i<=t; ++ i)
        solve(i);
    return 0;
}

转载于:https://www.cnblogs.com/aiterator/p/6850976.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
杭州电子科技大学在线评测系统(杭电OJ)中的题目1000-1100是一系列编程题,我将分别进行回答。 1000题是一个简单的入门题,要求计算两个整数的和。我们可以使用一个简单的算法,读取输入的两个整数,然后将它们相加,最后输出结果即可。 1001题是一个稍微复杂一些的题目,要求实现字符串的逆序输出。我们可以使用一个循环来逐个读取输入的字符,然后将这些字符存储在一个数组中。最后,我们可以倒序遍历数组并将字符依次输出,实现字符串的逆序输出。 1002题是一个求最大公约数的问题。我们可以使用辗转相除法来解决,即先求出两个数的余数,然后将被除数更新为除数,将除数更新为余数,直至两个数的余数为0。最后的被除数就是最大公约数。 1003题是一个比较简单的排序问题。我们可以使用冒泡排序算法来解决,即每次比较相邻的两个元素,如果它们的顺序错误就交换它们的位置。重复这个过程直至整个数组有序。 1100题是一个动态规划问题,要求计算给定序列中的最长上升子序列的长度。我们可以使用一个数组dp来保存到达每个位置的最长上升子序列的长度。每当遍历到一个位置时,我们可以将其和之前的位置比较,如果比之前位置的值大,则将其更新为之前位置的值加1,最后返回dp数组的最大值即可。 以上是对杭电OJ1000-1100题目的简要回答,涉及了一些基本的编程知识和算法思想。希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值