本文已参与[新人创作礼]活动,一起开启掘金创作之路
Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目描述
这是3月29日代码源div2的每日一题。
数位计算 - 题目 - Daimayuan Online Judge
给出一个整数 n,请解决下面的问题:
使 f(x)=(不超过 xx 且与 xx 具有相同位数的正整数的个数)。
求出 f(1)+f(2)+...+f(n) ,结果对 998244353 取模。
输入格式
一个整数 N。
输出格式
一个整数——上面问题的答案,并对 998244353 取模。
样例输入1
16
样例输出1
73
样例解释:对从 1 到 9 的每个 x,不超过 x 且与 x 具有相同位数的正整数有 1,2,..,x,因此,f(1)=1,f(2)=2,...,f(9)=9。对从 10 到 16 的每个 x,不超过 x 且与 x 具有相同位数的正整数有 10,11,..,x,因此,f(10)=1,f(11)=2,...,f(16)=7。所以答案为 73。
数据规模
所有数据保证 1≤N<10^18,且 N 是整数。
问题分析
这题其实思路并不难想,但我被取模的问题卡了一小时!一小时哎!
这里说的也很清楚了,f(x)的意思是,位数和x相等,但值大小不超过x的正整数的数量。那么f(x)的值就是:x-(当前位数的最小值)+1。比如f(152)=152-100+1=53;然后我们要算的是f(1)到f(x)的值的总和。
如果你此时想的是计算每个f()的值加一起,请注意这里x最多有10e18,妥妥超时。但你要是聪明点,应该就能想到了,在x和x-1位数相等的情况下,f(x)=f(x-1)+1,这是一个公差为1的等差数列!而因为每个数位的f最小都是1(比如f(1),f(10),f(100)等),所以这就是个首项为1,公差为1的等差数列,当然前提是数位相同的情况下,那我们只要算出每个数位的等差数列前n项和即可,如果题目给的N不够当前数位的最大值了,那就以N做尾。计算所有前n项和的总和就是我们要的结果(几点取模)。
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int MOD = 998244353;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
unsigned long long n, res = 0, num = 0, power = 10;
cin >> n;
while (1)
{
if (n > power - 1)
{
ll x = (1 + power - power / 10) % MOD;
ll y = ((power - power / 10)) % MOD;
res =res+ (x*y/2)%MOD;
res %= MOD;
}
else
{
ll x = (1 + n - power / 10 + 1) % MOD;
ll y = ((n - power / 10 + 1)) % MOD;
res = res+ (x*y/2)%MOD;
res %= MOD;
break;
}
power *= 10;
}
cout << res%MOD << endl;
return 0;
}