CodeForces - 1327E(dp)

题目大意

给定n(n < 2 * 105),统计从 0 到 10n-1的所有数字(有前导零)中,统计不同长度的数字块的个数。

举个栗子:
9987654321 和 1999999999 两个数字中
len = 1 的数字块的个数为:9(前边那个数里有八个,后边有一个)
len = 2 的数字块的个数为:1
len = 9 的数字块的个数为:1

分析

先考虑最特殊的情况 n = len = x的时候,也就是n个数字全部相同,显然不管x取值为多少答案都是10

eg:x = 3 时,len = 3 的情况有:
000,111,222,333,444,555,666,777,888,999

也就是说 (n=1,len=1)与(n=2.len=2)与(n=3,len=3)与(n=x,len=x)全部都是等价的
进而我们去想,还有没有其他的等价关系,比较容易想到的是(n=i,len=j)与(n=i-1,len=j-1)是等价的
举个栗子:(n=4,len=3)可以当作(n=3,len=2)中,每个块都再插入一个和这个块相同的数字对应关系如下:
331 -->> 3331
332 -->> 3332
333 -->> 3333
334 -->> 3334
335 -->> 3335
336 -->> 3336
337 -->> 3337
338 -->> 3338
339 -->> 3339
330 -->> 3330………… 列举一部分

到这里是不是就应该有这样一种奇妙想法(DP),如下图
在这里插入图片描述
不过,现在有一个很明显的问题,如上图中 (n=4,len=1)没办法从 (n=3,len=0)得到,毕竟 len = 0 不符合题意哎
题目有个隐藏的条件
还是拿上图的例子,从 0000 到 9999 一共出现多少个数字?
答案是 4 * 104

那么就有:
(n=4,len=1) = 4 * 10^4 - (n=4, len=2) * 2 - (n=4, len=3) * 3 - (n=4,len=4)*4;

我们先定义一个sum[n] = dp[1] + dp[2] + …… + dp[n];
再定义一个tot[n] = sum[n-1] + sum[n-2] + …… + sum[1];
这时候就出现递归式:tot[n] = tot[n-1] + sum[n-1];

现在把上边那个图给稍作修改
在这里插入图片描述
现在就很明显了

(n=4,len=1)=  all[n] - (tot[n-1] + sum[n-1])

代码

#include <bits/stdc++.h>
#define ll long long
const ll mod = 998244353;
const int maxn = 2e5 + 5;
using namespace std;

ll dp[maxn] = {0, 10};

int main(){
 
	ios::sync_with_stdio(false);
 
 	int n;
 	cin >> n;
 
 	 // 初始化 
	 ll tot = dp[1];      
	 ll sum = dp[1];      // sum = dp[1] + ... + dp[n] 
	 ll all = 100;        // 当前状态下一共有多少数字 ,从n=2开始 
	      
	 for(int i = 2; i <= n; i++){
	        dp[i] = ((all * i - tot - sum) % mod + mod) % mod;   //((x - y)% mod + mod) % mod  是取模的一个技巧 避免答案出现负数 
	        sum = (sum + dp[i]) % mod;                        // 更新 sum 
	        tot = (tot + sum) % mod;                          // 更新 tot 
	        all = (all * 10) % mod;               // all = 10^i; 
	 }
	 
	 for(int i = n; i >= 1; i--) cout << dp[i] << " ";    //输出的时候要倒这输 dp[1]里边存的是 (n=n,len=n)的答案; 
	
	 return 0;
}

第一次写博客,大佬轻喷!

猜你喜欢

转载自blog.csdn.net/mldl_/article/details/105951120