题面分析(题目写了啥)
题目要求将从 1 0 n − 1 10^{n−1} 10n−1 到 1 0 n − 1 10^n−1 10n−1 的所有 n n n 位数按照 m o d k mod\ k mod k 的结果分成 k k k 组,并计算每一组的个数。由于直接枚举肯定不行(因为 n n n 可以达到 5000 5000 5000),我们需要用差分思想+数学解决这个问题。
难点解析(写到一半不会了)
- 模 k k k 的分布规律
从 1 0 n − 1 10^{n−1} 10n−1 到 1 0 n − 1 10^n−1 10n−1 的所有 n n n 位数,这些数在模 k k k 下的分布是有循环性的,因此可以用差分算法。
- 计算每个 m o d k mod\ k mod k 的余数的个数
总数:从 1 0 n − 1 10^{n−1} 10n−1 到 1 0 n − 1 10^n−1 10n−1 的数的总数是 1 0 n − 1 0 n − 1 10^n−10^{n−1} 10n−10n−1。
m o d k mod\ k mod k 的分布:这些数在 m o d k mod\ k mod k 下的分布是均匀的,但仍会出现边界问题,处理方法也很简单,如下:
- 边界处理
最小值: 1 0 n − 1 m o d k 10^{n−1}\ mod\ k 10n−1 mod k
最大值: 1 0 n − 1 m o d k 10^n−1\ mod\ k 10n−1 mod k
代码结构(可对照下文食使用)
- fp函数
快速幂模板结构,快速幂详细了解请查阅这里,这里不做过多解释。
- zong变量
计算 1 0 n − 1 m o d k 10^{n−1}\ mod\ k 10n−1 mod k ,确定边界。
- sheng变量
计算符合题意的量,即每个 m o d k mod\ k mod k 的余数的个数。
- ans数组
遍历每个 m o d k mod\ k mod k 的余数,根据边界情况调整每个组的个数。结果对 100000007 100000007 100000007 取模后输出即可。
激动人心的代码时刻!
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int M = 100000007; //模
int ans[1000005]; //答案数组
// 快速幂函数模板
int fp(int a,int b,int mod){
a %= mod;
int ans = 1;
while(b){
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
signed main(){
ios::sync_with_stdio(0);
int n, k;
cin >> n >> k;
// 计算 10^(n-1) 的相关值
int zong = (fp(10,n - 1,k) - 1 + k) % k; // 10^(n-1) % k
int sheng = (fp(10,n - 1,M) - 1 - zong + M) * fp(k,M - 2,M) % M;
// 初始化答案数组
for(int i = 0; i < k; i++){
ans[i] -= sheng;
if (i != 0 && i <= zong) ans[i]--;
}
// 计算 10^n 的相关值
zong = (fp(10,n,k) - 1 + k) % k; // 10^n % k
sheng = (fp(10,n,M) - 1 - zong + M) * fp(k,M - 2,M) % M;
// 更新答案数组输出
for(int i = 0;i < k;i++){
ans[i] += sheng;
if (i != 0 && i <= zong) ans[i]++;
ans[i] = (ans[i] + M) % M; // 确保结果为正
cout << ans[i] << " ";
}
return 0; //金星:完美V
}