1327E Count The Blocks(组合数学)
题意: 对于一个数字串 x x x,可以看做是由多个连续相同的数字块组成的,比如 x = 00027734000 x = 00027734000 x=00027734000,其长度为 3 3 3 的数字块有两个( 000 000 000、 000 000 000),长度为 2 2 2 的数字块有一个( 77 77 77),长度为 1 1 1 的数字块有 3 3 3 个( 2 2 2、 3 3 3、 4 4 4)。现在给整数 n n n,表示有 0 0 0 到 1 0 n − 1 10^n-1 10n−1 共 1 0 n 10^n 10n 个数字,现在要求这些数字中长度为 i ≤ [ 1 , n ] i \le [1, n] i≤[1,n] 的数字块的个数,答案对 998244353 998244353 998244353 取模。
范围: 1 ≤ n ≤ 2 e 5 1 \le n \le 2e5 1≤n≤2e5
分析:直接枚举所有的数字进行统计肯定是不行的,根据数据范围应该需要对每种长度的数字块,通过公式计算出其数量。
因为数字块是连续相同的数字,因此可以直接看做是一个点。比如当前 n = 4 , i = 4 n = 4,i = 4 n=4,i=4,相当于 n = 1 , i = 1 n = 1, i = 1 n=1,i=1,总方案数为 10 10 10( 0 , 1 , 2 , 3...9 0,1,2,3...9 0,1,2,3...9),对应 ( 0000 , 1111 , 2222 , . . . 9999 0000,1111,2222,...9999 0000,1111,2222,...9999)。若当前 n = 4 , i = 2 n = 4, i = 2 n=4,i=2,相当于 n = 3 , i = 1 n = 3, i = 1 n=3,i=1…因此这些问题都可以转换成 n = x , i = 1 n = x, i = 1 n=x,i=1 的情况。
那么我们只要求当 n = x , i = 1 n = x,i = 1 n=x,i=1 的答案。
例如 n = 4 , i = 1 n = 4, i = 1 n=4,i=1。
① 这个点在第一位,那么方案数为 10 ∗ 9 ∗ 10 ∗ 10 10*9*10*10 10∗9∗10∗10
② 这个点在第二位,那么方案数为 10 ∗ 9 ∗ 9 ∗ 10 10*9*9*10 10∗9∗9∗10
③ 这个点在第三位,那么方案数为 10 ∗ 9 ∗ 9 ∗ 10 10*9*9*10 10∗9∗9∗10
④ 这个点在第四位,那么方案数为 10 ∗ 9 ∗ 10 ∗ 10 10*9*10*10 10∗9∗10∗10
我们可以发现除了两端的点,中间位置的方案数都相同,可以只计算一次。
对于中间位置的点,该点的数字方案有 10 10 10 种,与之相邻的两个点方案数为 9 9 9,其余点的方案数为 10 10 10,故方案数为 10 ∗ 9 ∗ 9 ∗ 1 0 n − 3 = 81 ∗ 1 0 n − 2 10*9*9*10^{n-3} = 81*10^{n-2} 10∗9∗9∗10n−3=81∗10n−2,而中间位置的点有 n − 2 n-2 n−2 个,所以中间所有位置的总方案为 ( n − 2 ) ∗ 81 ∗ 1 0 n − 2 (n-2)*81*10^{n-2} (n−2)∗81∗10n−2。
对于两端的点,该点的数字方案有 10 10 10 种,与之相邻的一个点方案数为 9 9 9,其余点的方案数为 10 10 10,故方案数为 10 ∗ 9 ∗ 1 0 n − 2 = 9 ∗ 1 0 n − 1 10*9*10^{n-2} = 9*10^{n-1} 10∗9∗10n−2=9∗10n−1,有左右两个端点,所有总方案数为 18 ∗ 1 0 n − 1 18*10^{n-1} 18∗10n−1。
因此,对于所有的长度 i i i,我们都可以用上述的公式在 O ( 1 ) O(1) O(1) 的时间内算出其方案数,总体时间复杂度为 O ( n ) O(n) O(n)。
Code:
#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
const int MAXN = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;
const double eps = 1e-9;
const double PI = acos(-1.0);
int n;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
int num[MAXN];
signed main()
{
// 预处理一下,也可以不预处理
num[0] = 1;
for (int i = 1; i < MAXN; i++)
{
num[i] = num[i - 1] % MOD * 10 % MOD;
}
n = read();
for (int i = 1; i <= n; i++)
{
if (i > 1)
cout << " ";
int len = n - i + 1; // 区间缩点后总点数
int ans;
// 2个点以上套公式
if (len >= 2)
{
ans = (len - 2) % MOD * 81 % MOD * num[len - 2] % MOD + 18 * num[len - 1] % MOD;
}
// 1个点只有10种方案数
else
{
ans = 10;
}
cout << ans % MOD;
}
cout << endl;
return 0;
}
【END】感谢观看