10.12 模拟赛

10.12 模拟赛

T1

首先有一个显然可以得到的结论,每次减去最大值一定最优。

只会最无脑的做法。。每次减去最大值,复杂度$O(n)$,虽然减了一些枝但还是显然过不去

其实感觉60分做法就很有意思了。

对于$n \leq 10^{12}$

考虑分开考虑前六位和后六位,这个时候前六位的变化只有$O(\sqrt n)$次。

考虑当前六位的最大值为某一个值时,后六位分别需要多少次才可以变成负数,以及会变成多少

复杂度是$O(\sqrt n)$

对于$n \leq 10^{18}$

也是一个挺奇妙的搞法吧?

类似于前面根号的做法,考虑用$f[i][j][k]$,$i$位数,表示当前$i - 1$位都是9,个位是$j$,且每次操作如果数位最大值小于$k$就减去$k$否则就减去数位最大值,需要多少次会减成负数,同时还要记录负数是多少。注意,每次减的都是一到九的一个数,所以最后得到的数仍然是一个前一段是9的数。

这个式子递推的过程其实就是一个模拟的过程,就是

1
2
3
4
5
9 | 9999.....999 j
8 | 9999.....999 j'
7 | 9999.....999 j''
...
0 | 9999.....999 j'''''...

这样一层一层推的,每次剩下的j都是由上一次剩下的来的。


这题更加难在于怎么统计答案,但是题解没写。。

我们可以先把这个数字减成 fo 的形式,这个时候就可以愉快地统计答案了

而减法就是一个模拟的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
using namespace std;
#define int long long
typedef long long ll;
#define MAXN 1006
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define inf 0x3f3f3f3f
#define cmx( a , b ) a = max( a , b )
#define cmn( a , b ) a = min( a , b )
#define upd( a , b ) ( a = ( a + b ) % P )
//#define P 1000000007
int n;
int f[19][10][10] , re[19][10][10];
void init() {
for( int i = 0 ; i <= 9 ; ++ i )
for( int j = 0 ; j <= 9 ; ++ j )
if( i < j )
f[1][i][j] = 1 , re[1][i][j] = 10 + i - j;
else
f[1][i][j] = 2 , re[1][i][j] = 10 - j;
for( int i = 2 ; i <= 18 ; ++ i )
for( int j = 0 ; j <= 9 ; ++ j )
for( int k = 0 ; k <= 9 ; ++ k ) {
re[i][j][k] = j;
for( int s = 9 ; s >= 0 ; -- s )
f[i][j][k] += f[i-1][re[i][j][k]][max( s , k )],
re[i][j][k] = re[i-1][re[i][j][k]][max( s , k )];
}
}
int num[19];
int make_your_dream_come_true( int x ) {
int fk = 0;
while( x )
num[++ fk] = x % 10 , x /= 10;
int cur = num[1] , ans = 0;
for( int i = 2 ; i <= fk ; ++ i ) {
while( num[i] != 9 ) {
int mx = 0;
for( int j = i ; j <= fk ; ++ j ) cmx( mx , num[j] );
if( !mx ) break;
ans += f[i - 1][cur][mx];
cur = re[i - 1][cur][mx];
-- num[i];
for( int j = i ; num[j] < 0 ; ++ j )
num[j] = 9 , -- num[j + 1];
}
}
for( int i = fk ; i >= 2 ; -- i )
while( num[i] )
ans += f[i-1][cur][num[i]] , cur = re[i-1][cur][num[i]] , -- num[i];
if( cur ) ++ ans;
return ans;
}
signed main() {
cin >> n;
init();
cout << make_your_dream_come_true( n ) << endl;

}
/*
* Things you should pay attention to
* inf is big enough?
* out of bounds?
* long long ?
*/

T2

水题,就是板子的矩形数点,就不放了

T3

李超线段树板子,先挖坑

文章作者: yijan
文章链接: https://yijan.co/old6/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog