luogu P13729 【MGVOI R1-A】超级奇数(odd)

题意

对于一个数,求将其转化为在十进制下所有位上是奇数所需要的最小代价。

思路

首先看样例,这种题我习惯先看样例来判断自己最初的想法,并且来向下做。

其实我第一眼看到这题是想在遇到某一位是偶数时将这一位 \(+1\)。但是看到样例后就放弃了这种做法,可见样例在这种题有多重要。

看第一组样例
input:

3
7
16
23

output:

0
1
8

发现,对于第二个数,它到最小的超级奇数只进行了 \(+1\) 操作。这并不难模拟。
但是,对于第三个数,它到最小奇数却不是我刚开始所说的遇到偶数就 \(+1\),而是 \(+8\),也就是说,我们刚开始的猜测是错误的。
那么我们重新观察第三个数,可见,\(23 + 8 = 31\)。这时候我就有个猜想,是否是遇到偶数,就将其后面每一位的数全部变成 \(1\),假设这位的数字是 \(a\),则它自己变成 \(a + 1\)。那么求最小花费就相当于求位数能到这一位数的且每一位全部为 \(1\) 的数与这位前面每一位所形成的数的差。如这个样例就相当于 \(11 - 3 = 8\)

因为时间明显不足我们证明,所以考虑观察第二组样例进行判断。
input:

8
2
82
128
136
13365
139454
310111
975319

output:

1
9
3
1
6
57
1000
0

只看第二个和第三个数,经过转换,明显符合之前的猜测。所以就可以开始写代码了(^^)!

代码

很明显,对于每一个输入的 \(a\),进行拆位,如果遇到了一位是偶数,就将 \(ans\) 赋成前面所说的式子,至于每一位都是 \(1\) 的数,则可以进行预处理。

因为我使用了快速幂,所以总时间复杂度应约为 \(O(t \log\log a)\)

std

#include<bits/stdc++.h>
using namespace std;
int t;
long long l1[13];
long long qpow(long long x, long long y)
{
	long long res = 1;
	while(y)
	{
		if(y & 1) res *= x;
		x *= x;
		y >>= 1;
	}
	return res;
}
long long solve(long long a)
{
	long long res = 0,i = 0,ans = 0;
	while(a)//拆位
	{
		if(((a % 10) & 1) == 0) ans = l1[i + 1] - res;//如果是偶数,就直接赋值,这样 ans 就能直接到最高位的
		//因为 i 是从 0 开始的,所以要 +1
		res = res + (a % 10) * qpow(10,i);//求算上这位所组成的数
		//因为我是从后向前判断的,如果求直接res则会有所偏差,所以这里使用了快速幂直接加
		i ++;//加位数
		a /= 10;//判断下一位
	}
	return ans ;//返回
}
int main()
{
//	freopen("odd5.in","r",stdin);
//	freopen("out.ans","w",stdout);
	ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
	cin >> t;
	for(int i = 1;i <= 12;i ++) l1[i] = l1[i - 1] * 10 + 1;//预处理每一位都是 1 的数
	while(t --)
	{
		long long a,b;cin >> a;//输入a
		b = solve(a);//求代价 即b
		cout << b << '\n';//输出
	}
	return 0;
}
posted @ 2025-09-19 10:47  medal_dreams  阅读(19)  评论(0)    收藏  举报