代码源:676、整除光棍

138 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情logo.png

题目描述

这是4月4日代码源div2的每日一题。

知识点:高精度除法

整除光棍 - 题目 - Daimayuan Online Judge

这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由 1 组成的数字,比如 1、111、111、1111 等。传说任何一个光棍都能被一个不以 5 结尾的奇数整除。比如, 111111 就可以被 13 整除。 现在,你的程序要读入一个整数 x ,这个整数一定是奇数并且不以 5 结尾。然后,经过计算,输出两个数字:第一个数字 s,表示x 乘以 s 是一个光棍,第二个数字n是这个光棍的位数。这样的解当然不是唯一的,题目要求你输出最小的解。

提示:一个显然的办法是逐渐增加光棍的位数,直到可以整除 x为止。但难点在于,s可能是个非常大的数 —— 比如,程序输入 31 ,那么就输出 3584229390681和 15 ,因为 31 乘以 3584229390681 的结果是 111111111111111 ,一共 15 个 1。

输入格式

输入在一行中给出一个不以 5 结尾的正奇数 x(x<1000)。

输出格式

在一行中输出相应的最小的 s 和 n,其间以 1 个空格分隔。

样例输入

31

样例输出

3584229390681 15

问题解析

要。。要被除掉哩。

很简单的题,他说x可以整除一个光棍数,那我们就直接枚举所有光棍数就可以了,看那个光棍数能被x整除,最后输出枚举的光棍数长度和商即可。但首先通过题目我们可以看出当x为31是,得到的s已经相当大了,就像题目说的,s可能是一个相当大的数,那么这个光棍数肯定也是相当大的,至少long long也塞不下了,那么直接枚举光棍数就显得做不到了。这里就要用到高精度的算数了,这类是当运算数很大时,大到任何一个数据也不能塞下它时用到的算数方法。

我们可以把一个数的每一位存入数组里,数组我们是可以开很大的,开到1e6也没问题,这就意味着我们能存一个1e6位的数(你可能没有概念,1亿也才9位数)。我们从长度1开始枚举光棍数,即每次往数组插入一个1,然后进行运算,如果不能整除,我们就继续往数组插入1,知道整除为止。

那么高精度除法我们该怎么算,这里说的方法是一个很大的数(存入数组)除于一个小的数(正常int就能装下)。进阶版很大数除很大数的方法感兴趣的自行百度即可。

我们先把数组翻过来(不翻过来算的会很麻烦),即原本数组是1 2 3 4 5,变成5 4 3 2 1。然后我们用一个变量r每次从开头取出一个数,除去除数x,把除数结果存入数组中(此时除数可能是0,不用管他),运算一次后,r中存的就是余数,然后r乘10后再从数组取一个数加到自己身上,继续除x。当数组中所有的数都取出来之后,我们运算就结束了,此时另一个数组存的就是我们的结果,r仍然是余数。但是此时答案数组是反过来的,因为我们前面把数组反过来了,所以我们也要吧答案数组翻过来。但就像我们上面说的,前面几次的运算结果可能是0,直接翻过来答案会有问题,比如数组是0 0 1 2,直接反过来是2 1 0 0,但答案应该是21,所以我们要把前面的0删掉。

(但是这里翻转前后都是一样的,所以我们可以省去翻转的步骤!)

我们来模拟一下这个过程:1 1 1 1 1 1除去13,然后r一开始是0,每次从数组取一个数,

先取一个1,r=1,数组:1 1 1 1 1,把r/x的结果存入答案数组c中,第一次运算后,答案数组是:0,r是1;

再取一个1,r=11,数组:1 1 1 1,运算后,答案数组:0 0 ,r是11;

再取一个1,r=111,数组1 1 1,运算后,答案数组:0 0 8,r是7;

r=71,数组:1 1,运算后,答案数组:0 0 8 5,r是6;

r=61,数组:1,运算后,答案数组:0 0 8 5 4,r是9:

r=91,数组为空,运算后,答案数组:0 0 8 5 4 7

然后我们把答案数组前导0去掉后,答案就是8547。其实聪明的也可以看出来了,这就是小学教的除法。

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n';
const int N = 1000050, MOD = 1e9 + 7;
typedef long long ll;
int n;

vector<int> div(vector<int> A, int& r)
{
    vector<int>C;
    for (int i = 0; i < A.size(); i++)
    {
        r = r * 10 + A[i];
        C.push_back(r / n);
        r %= n;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && !C.back())C.pop_back();
    return C;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin >> n;
    vector<int>v,c;
    int r = 0,ans=0;
    do {
        r = 0;
        v.push_back(1);
        ans++;
        c = div(v, r);
    } while (r != 0);
    reverse(c.begin(), c.end());
    for (auto i : c)
    {
        cout << i;
    }
    cout << " " << ans << endl;
    return 0;
}

优化

当然我们也可以用如上方法简化一下:因为我们都是从数组拿1算,当没有1了时说明我们枚举的光棍数不够长,那么我们可以直接算,只要算出来的余数r不为0,我们就给r后接上一个1直到它被整除。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n';
const int N = 1000050, MOD = 1e9 + 7;
typedef long long ll;
int n;

void write(int x) {
    if (x > 9) write(x / 10);
    putchar(x % 10 | '0');
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin >> n;
    int r = 1,ans=1;
    while (r < n)
    {
        r *= 10;
        r += 1;
        ans++;
    }
    while (1)
    {
        write(r / n);
        r %= n;
        if (r == 0)break;
        ans++;
        r *= 10;
        r += 1;
    }
    putchar(' ');
    write(ans);
    return 0;
}