AtCoder Regular Contest 123

( ext{C - 1, 2, 3 - Decomposition})

解法

定义一个数为好数当且仅当它由 (1,2,3) 组成,(f(i)) 就是 (i) 的答案。

分而治之 的思想挺妙的,但我压根没想到。考虑将 (n) 分解成 (10p+r) 的形式(并非除数与余数),可以想到,(f (p)) 分解的每个好数再乘以 (10) 加上 (1,2,3) 就又是一个好数。

基于此,不妨令 (kle rle 3k),那么如果 (f(p)le k) 就意味着一定能找出解,即有 (1,2,3) 与每个好数匹配。注意不满足条件也不意味着一定无解,这点下文详细再谈。

考虑时间复杂度。

打表发现(f(n)le 5)。现在我们加以证明:转化一下,现在只用证明 (n) 一定能被分解成这样的 (p,r) 满足 (f(p)le 5)(5le rle 15)。我们可以归纳证明。首先当 (ile 15) 时一定有 (f(i)le 5),对于其它情况,你发现 (r) 的个位取值取到了 ([0,9]) 中每个数字!而我们又可以任意减去 (10) 的倍数,所以结论得证。

既然我们证明这样构造一定有解,就不用考虑不满足构造仍有解的情况了。

由于 (f(n)le 5)(5le rle 15),我们容易得出对于每个 (n)(p) 至多有两种取值,而迭代次数由于每次除以 (10) 非常小。所以这是可过的。

代码

#include <cstdio>

#define print(x,y) write(x),putchar(y) 

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}

#include <map>
using namespace std;

typedef long long ll;

map <ll,int> mp;

int f(ll n) {
	if(mp.count(n)) return mp[n];
	int ans=-1;
	for(ll k=1;k<=n;++k) {
		for(ll i=k;i<=k*3;++i) {
			if(n-i<0) break;
			if((n-i)%10==0) {
				if(f((n-i)/10)<=k) {
					ans=k; break;
				}
			}
		}
		if(~ans) break;
	}
	return mp[n]=ans;
}

int main() {
	mp[0]=0;
	for(int T=read(9);T;--T) {
		ll n=read(9ll);
		print(f(n),'
');
	}
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/15047330.html