蒟蒻来讲题,还望大家喜。若哪有问题,大家尽可提!
Hello, 大家好哇!本初中生蒟蒻讲解一下AtCoder Beginner Contest 295这场比赛的F题!
===========================================================================================
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).
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.
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
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.
6
22 23 234
0295 295 295
0 1 9999999999999999
2718 998244353 9982443530000000
869120 1234567890123456 2345678901234567
2023032520230325 1 9999999999999999
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在每一位上有多少个数:(注意:这里位数是从0开始的,这里k相当于前面的X)
于是我们就可以设i为从S在第k位第第i小的数,此处我们还是以S = 95为例 (注意:这里位数是从0开始的,这里k相当于前面的X)
这样,我们就能精准的计算出有没有越界(即为这个数有没有大于 X X X)
我们可以为了以防出现 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
#include
#include
#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;
}
今天就到这里了!
大家有什么问题尽管提,我都会尽力回答的!
吾欲您伸手,点的小赞赞。吾欲您喜欢,点得小关注!