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

李超线段树板子,先挖坑

原文地址:https://www.cnblogs.com/yijan/p/11662116.html