2023山东ICPC省赛Problem E. Math Problem

2023 山东 I C P C 省赛 P r o b l e m E . M a t h P r o b l e m \Huge{2023山东ICPC省赛Problem E. Math Problem} 2023山东ICPC省赛ProblemE.MathProblem

文章目录


比赛链接:Dashboard - The 13th Shandong ICPC Provincial Collegiate Programming Contest - Codeforces

官方题解:E - 数学问题 - SUA Wiki

题意

首先给出五个数字: n , k , m , a , b n,k,m,a,b n,k,m,a,b;然后可以对n执行进行以下两种操作任意次:

  • 选择一个整数 x ( 0 ≤ x ≤ k ) x(0 \le x \le k) x(0xk);令 n = k × n + x n=k\times n + x n=k×n+x,该操作每次花费 a a a枚金币,每次选择的 x x x可以不同。
  • 令$n=\left \lfloor \frac{n}{k} \right \rfloor ,该操作每次花费 ,该操作每次花费 ,该操作每次花费b$枚金币。

求将 n n n变为** m m m的倍数**最少需要花费几枚金币( 0 0 0是任何正整数的倍数)。

数据范围:

  • ( 1 ≤ n ≤ 1 0 18 1\leq n\leq 10^{18} 1n1018, 1 ≤ k , m , a , b ≤ 1 0 9 1\leq k, m, a, b\leq 10^9 1k,m,a,b109)

思路

跟据两种操作,我们会发现,若执行一次操作①后,再执行一次操作②;那么这两次操作可以相互抵消, n n n的值不变。

因此我们会发现,执行完操作①之后将不再会执行操作②。

跟据题目数据范围可知,操作①和操作②之和不会超过 200 200 200次,是比较小的。然后跟据上面的结论,我们可以暴力枚举先执行操作②的次数。

由于操作①有+x的情况,因此进行 p p p次操作①后, n n n的范围为: [ k p × n 0 , k p × ( n 0 + 1 ) − 1 ] [k^p\times n_0,k^p\times (n_0+1)-1] [kp×n0,kp×(n0+1)1](其中 n 0 n_0 n0 n n n完成除法操作时的值),因此只要 n n n在此区间即可停止乘法操作。

需要注意的是,在枚举的过程中,答案可能会超过long long范围,因此需要使用__int128__int128无法直接读入和输出,只能使用快读读入)。

其实我们在计算 n n n的范围时,只需要将其对 m m m取模即可,因为我们只需要判断当前 n n n的值距离 m m m的倍数的距离即可。

标程

#include<bits/stdc++.h>

using namespace std;

#define IOS ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
#define int long long 
#define ULL unsigned long long 
#define PII pair<int, int>
#define lowbit(x) (x & -x)
#define Mid ((l + r) >> 1)
#define ALL(x) x.begin(), x.end()
#define endl '\n'
#define fi first 
#define se second

const int INF = 0x7fffffff;
const int Mod = 1e9 + 7;
const int N = 2e5 + 10; 

void Solved() { 
    int n, k, m, a, b; cin >> n >> k >> m >> a >> b;
    if(n % m == 0) {cout << "0\n"; return;}
    if(k == 1) {cout << "-1\n"; return;}

    int res = 1e18, cost = 0;
    //双重循环,第一层枚举除的次数,第二层枚举乘的次数
    while(1) {
        int base = n % m, p = 1;
        for(int i = 0; ; i ++ ) {
            int d = (m - base) % m;//需要对m取模,表示距离每个m的倍数的位置
            // int d = m - base;
            // if(d == m) d = 0;
            if(d < p) {
                res = min(res, cost + i * a);
                break;
            }
            base = base * k % m;
            p = p * k;//对于x的累加不需取模
        }
        if(n == 0) break;
        n /= k;
        cost += b;
    }
    
    cout << res << endl;
}

signed main(void) {
    IOS

    int ALL = 1; 
    cin >> ALL;
    while(ALL -- ) Solved();
    // cout << fixed;//强制以小数形式显示
    // cout << setprecision(n); //保留n位小数

    return 0;
}
  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值