蒟蒻来讲题,还望大家喜。若哪有问题,大家尽可提!
Hello, 大家好哇!本初中生蒟蒻讲解一下AtCoder Beginner Contest 295这场比赛的F题!
===========================================================================================
F - substr = S
原题
Problem Statement
You are given a string
S
S
S consisting of digits and positive integers
L
L
L and
R
R
R for each of
T
T
T test cases. Solve the following problem.
For a positive integer
x
x
x, let us define
f
(
x
)
f(x)
f(x) as the number of contiguous substrings of the decimal representation of
x
x
x (without leading zeros) that equal
S
S
S.
For instance, if
S
=
S=
S= 22
, we have
f
(
122
)
=
1
f(122) = 1
f(122)=1,
f
(
123
)
=
0
f(123) = 0
f(123)=0,
f
(
226
)
=
1
f(226) = 1
f(226)=1, and
f
(
222
)
=
2
f(222) = 2
f(222)=2.
Find
∑
k
=
L
R
f
(
k
)
\displaystyle \sum_{k=L}^{R} f(k)
k=L∑Rf(k).
Constraints
1
≤
T
≤
1000
1 \le T \le 1000
1≤T≤1000
S
S
S is a string consisting of digits whose length is between
1
1
1 and
16
16
16, inclusive.
L
L
L and
R
R
R are integers satisfying
1
≤
L
≤
R
;
1
0
16
1 \le L \le R ; 10^{16}
1≤L≤R;1016.
Input
The input is given from Standard Input in the following format, where
c
a
s
e
i
\rm{case}_i
casei denotes the
i
i
i-th test case:
T
T
T
c
a
s
e
1
\rm{case}_{1}
case1
c
a
s
e
2
\rm{case}_{2}
case2
⋮
\vdots
⋮
c
a
s
e
T
\rm{case}_{\it{T}}
caseT
Each test case is in the following format:
S
S
S
L
L
L
R
R
R
Output
Print
T
T
T lines in total.
The
i
i
i-th line should contain an integer representing the answer to the
i
i
i-th test case.
Sample Input 1
6
22 23 234
0295 295 295
0 1 9999999999999999
2718 998244353 9982443530000000
869120 1234567890123456 2345678901234567
2023032520230325 1 9999999999999999
Sample Output 1
12
0
14888888888888889
12982260572545
10987664021
1
This input contains six test cases.
In the first test case,
S
=
S=
S= 22
,
L
=
23
L=23
L=23,
R
=
234
R=234
R=234.
f
(
122
)
=
f
(
220
)
=
f
(
221
)
=
f
(
223
)
=
f
(
224
)
=
⋯
=
f
(
229
)
=
1
f(122)=f(220)=f(221)=f(223)=f(224)=\dots=f(229)=1
f(122)=f(220)=f(221)=f(223)=f(224)=⋯=f(229)=1.
f
(
222
)
=
2
f(222)=2
f(222)=2.
Thus, the answer is
12
12
12.
In the second test case,
S
=
S=
S= 0295
,
L
=
295
L=295
L=295,
R
=
295
R=295
R=295.
Note that
f
(
295
)
=
0
f(295)=0
f(295)=0.
题目大意
本题是求
∑
k
=
L
R
f
(
k
)
\displaystyle \sum_{k=L}^{R} f(k)
k=L∑Rf(k)
f
(
k
)
=
k
f(k)=k
f(k)=k这个数转换为字符串后,出现S的个数
例如:
k
=
222
,
S
=
22
k=222,S=22
k=222,S=22
则
f
(
k
)
=
2
f(k)=2
f(k)=2
思路
本题让我们求
∑
k
=
L
R
f
(
k
)
\displaystyle \sum_{k=L}^{R} f(k)
k=L∑Rf(k)
那么可以利用前缀和的思想转化为求
∑
k
=
1
R
f
(
k
)
−
∑
k
=
1
L
−
1
f
(
k
)
\displaystyle \sum_{k=1}^{R}f(k) - \displaystyle \sum_{k=1}^{L - 1}f(k)
k=1∑Rf(k)−k=1∑L−1f(k)
所以我们的主要任务就是求 ∑ k = 1 X f ( k ) ( 1 ≤ X ≤ 1 0 16 ) \displaystyle \sum_{k=1}^{X}f(k)(1\le X \le 10^{16}) k=1∑Xf(k)(1≤X≤1016)
然后我们可以构造
k
k
k,对于有值的
f
(
k
)
f(k)
f(k)那么一定是包含
S
S
S的,那么我们可以枚举
S
S
S在哪一位,因为一共就只有16位。
如图:
情况一:S没有前导零
我们相应的计算S在每一位上有多少个数:(注意:这里位数是从0开始的,这里k相当于前面的X)
于是我们就可以设i为从S在第k位第第i小的数,此处我们还是以S = 95为例 (注意:这里位数是从0开始的,这里k相当于前面的X)
这样,我们就能精准的计算出有没有越界(即为这个数有没有大于 X X X)
情况二:S有前导零
我们可以为了以防出现 S = 05 , k = 1 , i = 0 S=05, k = 1, i = 0 S=05,k=1,i=0结果算成了 f ( 5 ) = 1 f(5) = 1 f(5)=1这样的情况发生
我们可以在前面补个S的前面补充一个1。如
S
=
05
S=05
S=05
i
=
1
,
k
=
3
i=1,k=3
i=1,k=3时,就是10500
所以我们不再是和情况一样是
i
−
1
i-1
i−1,而是
i
−
1
+
1
0
r
i-1+10^r
i−1+10r
r
=
k
+
1
−
∣
S
∣
r=k+1-|S|
r=k+1−∣S∣(
∣
S
∣
|S|
∣S∣表示S的长度)
例如,
S
=
05
,
i
=
151
,
k
=
3
S=05, i=151, k = 3
S=05,i=151,k=3时:
r
=
3
+
1
−
2
=
2
r=3+1-2=2
r=3+1−2=2
所以填写的就是:
i
−
1
+
1
0
r
=
150
+
1
0
2
=
150
+
100
=
250
i-1+10^r=150+10^2=150+100=250
i−1+10r=150+102=150+100=250
所以对应填入:
? | 05 | ? | ? |
---|---|---|---|
2 | 05 | 5 | 0 |
之后,我们可以用一个函数
n
u
m
b
e
r
(
i
,
k
,
s
)
number(i, k, s)
number(i,k,s)表示
k
k
k位填充
S
S
S后,第
i
i
i小的数。之后通过二分找到最大的没有超过边界的数,然后加上这个最大的
i
i
i即可。最后把答案累加!
具体看代码吧
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long
#define endl '\n'
#define psb(i) push_back(i)
#define ppb() pop_back()
#define psf(i) push_front(i)
#define ppf() pop_front()
#define ps(i) push(i)
using namespace std;
typedef pair<int, int> PII;
const int N = 2e1 + 10;
int T;
string s;
int l, r;
int p10[N];
inline int read()
{
int w = 1, s = 0;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9') s = s * 10 + c - '0', c = getchar();
return w * s;
}
inline void init()
{
p10[0] = 1;
for (int i = 1; i <= 16; i ++)
p10[i] = p10[i - 1] * 10;
}
int number(int i, int k, string s)
{
int r = k + 1 - s.size();
if (r < 0) return -1;
if (s[0] == '0') i += p10[r];
i --;
int p = i / p10[r]; //有前导零补得数
int q = i % p10[r];
return p * p10[k + 1] + stoll(s) * p10[r] + q;
}
inline int solve(int x, string s)
{
int res = 0;
for (int k = 0; k <= 15; k ++)
{
int first = number(1, k, s);
if (first == -1 || first > x) continue;
int l = 1, r = p10[16 - s.size()];
while (l <= r)
{
int fd = l + r >> 1;
if (number(fd, k, s) > x) r = fd - 1;
else l = fd + 1;
}
res += r;
}
return res;
}
signed main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
init();
cin >> T;
while (T --)
{
cin >> s >> l >> r;
cout << solve(r, s) - solve(l - 1, s) << endl;
}
return 0;
}
今天就到这里了!
大家有什么问题尽管提,我都会尽力回答的!
吾欲您伸手,点的小赞赞。吾欲您喜欢,点得小关注!