$("head").append('')

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\) 应该被后面的进位加一,但事实是我们只看了两个数字就润了,根本没有人来加它(那我缺的营养这块谁来给我补啊)。

那么要怎么解决呢?


接下来是我的正解思路:

首先讲一下坑点特判:

  1. 我们要先处理后缀 \(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;
  1. 如果不需要进位,可以直接输出 \(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

*/
posted @ 2025-10-12 15:31  lain_yc  阅读(11)  评论(0)    收藏  举报
$("head").append('')