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的数。
这个式子递推的过程其实就是一个模拟的过程,就是
9 | 9999.....999 j
8 | 9999.....999 j'
7 | 9999.....999 j''
...
0 | 9999.....999 j'''''...
这样一层一层推的,每次剩下的j都是由上一次剩下的来的。
这题更加难在于怎么统计答案,但是题解没写。。
我们可以先把这个数字减成 fo 的形式,这个时候就可以愉快地统计答案了
而减法就是一个模拟的过程。
#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
李超线段树板子,先挖坑