CCF-CSP 202209-1 如此编码
😸题目要求
🐈⬛问题背景
某次测验后,顿顿老师在黑板上留下了一串数字 23333 便飘然而去。凝望着这个神秘数字,小 P 同学不禁陷入了沉思......
🐈⬛问题描述
已知某次测验包含 n n n 道单项选择题,其中第 i i i 题 ( 1 ≤ i ≤ n ) (1 \leq i \leq n) (1≤i≤n) 有 a i a_i ai 个选项,正确选项为 b i b_i bi,满足 a i ≤ 2 a_i \leq 2 ai≤2 且 0 ≤ b i < a i 0 \leq b_i < a_i 0≤bi<ai。比如说, a i = 4 a_i =4 ai=4 表示第 i i i 题有 4 4 4 个选项,此时正确选项 b i b_i bi 的取值一定是 0、1、2、3 其中之一。
顿顿老师设计了如下方式对正确答案进行编码,使得仅用一个整数 m m m 便可表示 b 1 , b 2 , ⋅ ⋅ ⋅ , b n b_1,b_2,···,b_n b1,b2,⋅⋅⋅,bn。
首先定义一个辅助数组 c i c_i ci,表示数组 a i a_i ai 的前缀乘积。当 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n 时,满足:
c i = a 1 × a 2 × ⋅ ⋅ ⋅ × a i c_i=a_1×a_2×···×a_i ci=a1×a2×⋅⋅⋅×ai
特别地,定义 c 0 = 1 c_0=1 c0=1。
于是 m m m 便可按照如下公式算出:
m = ∑ i = 1 n c i − 1 × b i = c 0 × b 1 + c 1 × b 2 + ⋅ ⋅ ⋅ + c n − 1 × b n m=\sum_{i=1}^{n} c_{i-1}×b_i \\ \quad =c_0×b_1+c_1×b_2+···+c_{n-1}×b_n m=∑i=1nci−1×bi=c0×b1+c1×b2+⋅⋅⋅+cn−1×bn
易知, 0 ≤ m < c n 0 \leq m < c_n 0≤m<cn,最小值和最大值分别当 b i b_i bi 全部为 0 和 b i = a i − 1 b_i=a_i - 1 bi=ai−1 时取得。
试帮助小 P 同学,把测验的正确答案 b 1 , b 2 , ⋅ ⋅ ⋅ , b n b_1,b_2,···,b_n b1,b2,⋅⋅⋅,bn 从顿顿老师留下的神秘整数 m m m 中恢复出来。
🐈⬛输入格式
从标准输入读入数据。
输入共两行。
第一行包含用空格分隔的两个整数 n n n 和 m m m,分别表示题目数量和顿顿老师的神秘数字。
第二行包含用空格分隔的 n n n 个整数 a 1 , a 2 , ⋅ ⋅ ⋅ , a n a_1,a_2,···,a_n a1,a2,⋅⋅⋅,an,依次表示每道选择题的选项数目。
🐈⬛输出格式
输出到标准输出。
输出仅一行,包含用空格分隔的 n n n 个整数 b 1 , b 2 , ⋅ ⋅ ⋅ , b n b_1,b_2,···,b_n b1,b2,⋅⋅⋅,bn,依次表示每道选择题的正确选项。
🐈⬛样例说明
🎶样例1输入
cpp
15 32767
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
🎶样例1输出
cpp
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
🎶样例2输入
cpp
4 0
2 3 2 5
🎶样例2输出
cpp
0 0 0 0
🎶样例3输入
cpp
7 23333
3 5 20 10 4 3 10
🎶样例3输出
cpp
2 2 15 7 3 1 0
🎶样例3解释
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
a i a_i ai | 3 | 5 | 20 | 10 | 4 | 3 | 10 |
b i b_i bi | 2 | 2 | 15 | 7 | 3 | 1 | 0 |
c i − 1 c_{i-1} ci−1 | 1 | 3 | 15 | 300 | 3000 | 12000 | 36000 |
🐈⬛子任务
50% 的测试数据满足: a i a_i ai 全部等于 2,即每道题均只有两个选项,此时 c i = 2 i c_i=2^i ci=2i;
全部的测试数据满足: 1 ≤ n ≤ 20 1 \leq n \leq 20 1≤n≤20, a i ≥ 2 a_i \geq 2 ai≥2 且 c n ≤ 1 0 9 c_n \leq 10^9 cn≤109(根据题目描述中的定义 c n c_n cn 表示全部 a i a_i ai 的乘积)。
🐈⬛提示
对任意的 1 ≤ j ≤ n 1 \leq j \leq n 1≤j≤n,因为 c j + 1 , c j + 2 , ⋅ ⋅ ⋅ c_{j+1},c_{j+2},··· cj+1,cj+2,⋅⋅⋅ 均为 c j c_j cj 的倍数,所以 m m m 除以 q q q 的余数具有如下性质:
m % c j = ∑ i = 1 n c i − 1 × b i m\%c_j=\sum_{i=1}^{n} c_{i-1}×b_i m%cj=∑i=1nci−1×bi
其中 % \% % 表示取余运算。令 j j j 取不同的值,则有如下等式:
m % c 1 = c 0 × b 1 m\%c_1=c_{0}×b_1 m%c1=c0×b1
m % c 2 = c 0 × b 1 + c 1 × b 2 m\%c_2=c_{0}×b_1+c_{1}×b_2 m%c2=c0×b1+c1×b2
m % c 3 = c 0 × b 1 + c 1 × b 2 + c 2 × b 3 m\%c_3=c_{0}×b_1+c_{1}×b_2+c_{2}×b_3 m%c3=c0×b1+c1×b2+c2×b3
⋅ ⋅ ⋅ ··· ⋅⋅⋅
😸问题解决
🐈满分代码(含逐行代码解释)
🍭C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
//输入
int n, a[100001], b[100001];
long long int m;
cin >> n >> m;
for(int i = 0; i < n; i++){
cin >> a[i];
}
//计算
long long int c = 1;
long long int temp;
for(int i = 0; i < n; i++){
//计算过程按照题目要求的来
temp = c;
c *= a[i];
b[i] = ((m % c) - (m % temp)) / temp;
//计算m除以c的余数,然后减去m除以temp的余数,最后再除以temp
}
//输出
for(int i = 0; i < n; i++){
cout << b[i] << " ";
}
return 0;
}
🍭Python
python
#代码逻辑与C++完全相同,不再一一赘述
# 输入
n, m = map(int, input().split())
a = list(map(int, input().split()))
b = [0] * n
# 计算
c = 1
for i in range(n):
temp = c
c *= a[i]
b[i] = ((m % c) - (m % temp)) // temp
# 输出
for i in range(n):
print(b[i], end=" ")
🐈场景拓展
本题代码适用于一类与模运算相关的题型。这类题型通常涉及到对给定数值范围内的所有数进行某种操作,并且需要计算每个数在该操作下的结果。具体来说,这段代码适用于以下类型的题目:
- 数论问题:求解某个数在给定范围内的取值情况,如求解在区间[1, n]中能被a整除但不能被b整除的数的个数。
- 排列组合问题:求解某个数的阶乘、组合数等相关问题,如求解n个数的全排列中,m在某个位置的个数。
- 动态规划问题:求解某个状态的转移方程,如背包问题、最长公共子序列等。