P9215 [入门赛 #11] [yLOI2021] 扶苏与 1 (Hard Version) 题解
P9215 [入门赛 #11] [yLOI2021] 扶苏与 1 (Hard Version)
题目背景
本题与 Easy Version 的区别是:\(x\) 的范围不同,\(y\) 的长度限制不同。
请注意 Easy Version 和 Hard Version 不是严格的包含关系。
扶苏在 ICPC2022 EC Final 的比赛里,开局不到五分钟,就读假了一道题,把一道数位 DP 开成了简单签到,狠狠地演了队友一把。
为了不让读假了的题被浪费,所以这道题出现在了这里。
题目描述
扶苏给了你一个数字 \(x\),你需要给她一个数字 \(y\),使得在列竖式计算 \(x + y\) 时,能恰好产生 \(k\) 个进位。
你给出的 \(y\) 的长度不能超过 \(x\) 的长度。(注意,这条要求与 Easy Version 不同)
进位的含义是:在进行竖式加法运算时,如果位于同一列上的数字之和(加上低位向上可能存在的进位)比 \(9\) 大,则在结果的这一列上只保留这个和的个位数字,同时称这一位向它的高位产生了一个进位。
下图是一个竖式加法的例子,结果中标红的两位都向上产生了进位。

输入格式
本题单测试点内有多组测试数据。
第一行为一个整数 \(T\),代表测试数据组数。
接下来 \(T\) 组数据,每组数据只有两行,每行一个整数。
第一行的整数表示 \(x\)。
第二行的整数表示 \(k\)。
输出格式
本题采用 special judge 进行判题。
对每组数据,输出一行一个整数,表示你给出的 \(y\)。
如果有多个满足要求的 \(y\),你可以输出任何一个。但是必须保证如下三条限制:
- \(y\) 是正整数。
- \(y\) 不含前导 \(0\)。
- \(y\) 的长度不超过 \(x\) 的长度。
特别的,如果这样的 \(y\) 不存在,请你输出一行一个 \(\texttt{-1}\)。
输入输出样例 #1
输入 #1
5
1
1
14
1
514
2
1234
1
123456
6
输出 #1
9
8
516
7
877777
说明/提示
数据规模与约定
对全部的测试点,保证 \(0 \leq x < 10^{(10^4)}\),\(1 \leq T \leq 5000\),\(1 \leq k \leq 1+\log_{10}\max(1,x)\)。输入的 \(x\) 不含前导 \(0\)。
提示
输入的 \(x\) 可能会很大。如果说 \(x < 10^t\),则输入 \(x\) 的长度不会超过 \(t\)。数据规模中式子 $k \leq 1 + \log_{10}\max(1,x) $ 的含义是:\(k\) 不会超过 \(x\) 的长度。
题解
题意:
有 \(t\) 组数据,对于每一组数据,给出 \(x\),要求构造一个数 \(y\),使进行 \(x+y\) 运算时出现 \(k\) 个进位。
思路:
一看到这个题,我想的是从最高位开始看,向低位移动 \(k\) 次并填充上 \(9\),也就是 \(y\) 的最高 \(k\) 位都是 \(9\) 而剩下的是 \(0\),因为 \(0\) 一定被后面的进位变成 \(1\) 然后再加 \(9\) 进位,同时也满足 \(len(y)≤len(x)\) 的条件。
于是开心的打出来了:
#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
while(n--){
string a,s;int k,tagg=0,cnt=0;
cin>>a>>k;
int aa=a.size();
for(int i=aa-1;i>=0;i--){
if(a[i]=='0')cnt++;
else break;
}
if(aa-cnt<k){
putchar('-1\n');continue;
}
if(k==0){
putchar('0');
continue;
}
for(int i=0;i<aa;i++){
if(k==0){
putchar('0');
continue;
}
putchar('9');
k--;
}
putchar('\n');
}
}
结果嘛……自然是一发 \(0pts\) 收入囊中。
这个思路为什么不对呢?
我们观察以下数据:
1
900009
2
如果按照上述思路,那么第 \(2\) 位数字 \(0\) 应该被后面的进位加一,但事实是我们只看了两个数字就润了,根本没有人来加它(那我缺的营养这块谁来给我补啊)。
那么要怎么解决呢?
接下来是我的正解思路:
首先讲一下坑点特判:
- 我们要先处理后缀 \(0\),找出它们并暂时删去,因为后缀 \(0\) 一定不会被进位。
同时我们可以注意到,如果总长度减去后缀 \(0\) 个数已经小于 \(k\) 了,那么接下来可以被进位的位数一定也小于 \(k\),因此直接输出 \(-1\) 结束。
int aa=a.size();
for(int i=aa-1;i>=0;i--){
if(a[i]=='0')cnt++;
else break;
}
if(aa-cnt<k){
cout<<"-1\n";continue;
}
aa-=cnt;
- 如果不需要进位,可以直接输出 \(0\)。
if(k==0){
putchar('0');
continue;
}
说完了特殊,我们来向一般推广:
首先,还是从高位推向低位,如果已经没有 \(k\) 可以进位了,那么就要输出 \(0\);而对于原本就是 \(0\) 的数,先存着以后再用。
for(int i=0;i<aa;i++){
if(a[i]=='0'||k==0)s+='0';
else{
s+='9';
k--;
}
}
再推完第一遍之后,发现答案和刚刚的一模一样,于是带着鸭蛋回家再跑一遍,从低位向上,判断能不能让 \(0\) 进位:
for(int i=aa-1;i>=0;i--){
if(a[i]=='0'&&k>0){
s[i]='9';k--;
}
}
这样之后,我们就得到了正确的答案。
不过,要注意的是,最后输出的时候不要忘记被我们抛下的后缀 \(0\),以及多测要清空。
完整代码:
(马蜂构式,各位将就看)
#include<bits/stdc++.h>
using namespace std;
int n;
string a,s;int k,tagg=0,cnt=0,num=0;
int main(){
cin>>n;
while(n--){
tagg=0,cnt=0,num=0;s="";
cin>>a>>k;
int aa=a.size();
for(int i=aa-1;i>=0;i--){
if(a[i]=='0')cnt++;
else break;
}
if(aa-cnt<k){
cout<<"-1\n";continue;
}
aa-=cnt;
if(k==0){
putchar('0');
continue;
}
for(int i=0;i<aa;i++){
if(a[i]=='0'||k==0)s+='0';
else{
s+='9';
k--;
}
}
for(int i=aa-1;i>=0;i--){
if(a[i]=='0'&&k>0){
s[i]='9';k--;
}
}
cout<<s;
for(int i=1;i<=cnt;++i)putchar('0');puts("");
}
}
/*
1
900009
2
*/

浙公网安备 33010602011771号