[POI 2017] Podzielno 题解
[POI 2017] Podzielno 题解
\(\text{Step1}\) 思路引入
不难联想到,生活中我们一个数是不是 \(9\) 的倍数判别方法是判断它的各位数字之和是否是 \(9\) 的倍数,这正是题目中 \(B = 10\) 的情况。
于是我们便大胆猜想,是否对于所有 \(X\) 的各位数字之和是 \(B-1\) 的倍数的时候都满足 \(X\) 为 \(B-1\) 的倍数。
\(\text{Step2}\) 深入思考
形式化的表示上面的命题:
\[X\equiv \sum_{i=0}^{B-1}i\times a_i\pmod{B-1}
\]
这里的 \(\sum\limits_{i=0}^{B-1}i\times a_i\) 表示 \(X\) 的各位数字之和。
根据事实,该命题显然正确,下面给出证明:
证明:
\[\begin{align*} X &= 1\times a_1\times B^0+2\times a_2\times B^1+\cdots+n\times a_n\times B^{n-1}\\ &= \sum_{i=0}^{B-1}i\times a_i\times B^{i-1} \end{align*} \]显然,\(B^{i-1}\equiv B^0\pmod{B-1}\),即 \(B^{i-1}\equiv1\pmod{(B-1)}\) 那么
\[\begin{align*} X&\equiv\sum_{i=1}^{B-1}i\times a_i\times B^{i-1} \pmod{\left(B-1\right)}\\ &\equiv \sum_{i=1}^{B-1}i\times a_i \pmod{\left(B-1\right)} \end{align*} \]证毕。
有了上面的结论,我们就可利用 \(\sum\limits_{i=0}^{B-1}i\times a_i\) 判断并寻找 \(B-1\) 的倍数。
接下来就是满足 \(X\) 最大的条件,对于 \(B\) 进制数显然位数越多且高位数字越大 \(X\) 就越大,那么我们尽量用上所有的数字并让大数字尽量在高位上。如果 \(\sum\limits_{i=0}^{B-1}i\times a_i\) 是 \(B-1\) 的倍数最好,那不是又该怎么办?注意到 \(a_i\ge1\),所以我们直接删掉 \(\sum\limits_{i=0}^{B-1}i\times a_i \pmod{\left(B-1\right)}\) 的余数部分即可,满足了数字位数尽可能多的前提。这样对修改后的 \(a_i\) 进行一次前缀和,然后二分查找 \(k\) 即可。
\(\text{Step3}\) 付诸实践
$\text{Code}$
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
int b, q;
int a[N];
long long sum;
long long s[N];
template <typename type>
void read(type &res) {
type x = 0, f = 1;
char c = getchar();
for (; c < 48 || c > 57; c = getchar()) if (c == '-') f = ~f + 1;
for (; c > 47 && c < 58; c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
res = f * x;
}
int main() {
read(b), read(q);
for (int i = 0; i < b; i++) read(a[i]);
for (int i = 0; i < b; i++) sum += 1ll * a[i] * i; // 各位数字求和
if (sum % (b - 1)) a[sum % (b - 1)]--; // 删去余数部分
for (int i = 0; i < b; i++) s[i] = s[i - 1] + a[i]; // 前缀和
for (int i = 1; i <= q; i++) {
long long k;
read(k); k++;
if (k > s[b - 1]) puts("-1"); // 若数字位数没有 k 个,输出 -1 即可
else printf("%lld\n", lower_bound(s, s + b, k) - s); // 二分查找 k
}
return 0;
}

浙公网安备 33010602011771号