CF1373E Sum of Digits

网上的好多题解没有看懂,题解中的一些"显然"的结论也觉得完全不显然…

我们如果枚举这个数列的第一个数字的个位,那么,如果存在这个数列的话,是一定可以构造出这个数列的。(因为k<=9,所以数列长度<=10,最多只会进位一次)
1.当这个数列不存在进位的时候,且第一个数的最后一位为now时,就可以表示为:a+now,a+now+1,a+now+2, … ,a+now+k。(其中,因为不进位,所以now+k<=9,而a则表示他们相同的 最高位到十位)
对于这种情况,知道now,k和n后,就能够求得了。
	if (now+k<=9)
		{
    
    
			int last=n-(now*(k+1)+(1+k)*k/2);
			if (last<0 || last%(k+1)!=0) continue;
			last/=(k+1);
			int num=now,bin=1;
			while (last)
			{
    
    
				bin*=10;
				if (last>=9) last-=9,num=bin*9+num;
				else num=bin*last+num,last=0;
			}
			ans=min(ans,num);
		}
2.当这个数列存在进位时,该怎么求呢?
有一篇题解这样写到:
最后我们只要判断那些x+k>9的数需要进位。
处理很简单只要把倒数第二位填8(避免了进位)。
再倒着贪心填9即可。
但是我一直不懂为什么可以这样贪心。这样贪心的最优性,是可以理解的,但是正确性,一直不理解,也不会证明,该如何保证这样填,一定可以覆盖到所有的情况呢?
我们先设:第一个数的最高位到十位,为a,最后一个数的最高位到十位,为b。(a+1=b,因为是进位过后了)
那么,对于数位的和,a和b之间有什么关联呢?
如果a和b之间没有进位,那么sum(a)+1=sum(b)。
如果有进位,那么a的最后若干位肯定是连续的9,当最后有i个连续的9时,sum(a)-i*9+1=sum(b)。
所以我们可以再枚举最后有多少个连续的9,这个得到数列的第一个数。
有同学可能会问,为什么要枚举最后有多少个连续的9,这不是也是结论吗?不也没有证明吗?
尽量多填9,这应该是显然的吧?但是我们不确定,填了几个9以后,是否一定有相应的数列存在,所以我们枚举9的个数。
else
		{
    
    
			int l=now; int len1=9-l+1;
			int r=(k+1)-len1-1; int len2=r-0+1;  
			//没有进位时的个位表示为:now-->9 ; 
			//进位后的个位表示为:0-->len2
			int last=n-(now*len1+(0+len1-1)*len1/2);
			if (last<0) continue;
			last=last-(0+r)*(r+1)/2;
			if (last<0) continue;
			
			for (register int i=0; i<=17; ++i)
			{
    
    
				int lastt=last+len2*9*i-len2;
				//由进位时,sum(a)-i*9+1=sum(b)得到
				//我们就把这个-i*9+1加回去,这样求得的答案就是第一个数的 最高位到十位了 
				if (lastt%(len1+len2)!=0) continue;
				lastt/=(len1+len2);
				if (lastt<9*i) continue;
				
				int num=now,bin=1;
				for (register int j=1; j<=i; ++j) 
				{
    
    
					bin*=10;
					lastt-=9;
					num=bin*9+num;
				}
				//一段连续的9以后,如果剩下的数位和还是比9大,那么我们是不可以填9了
				//不然就不满足枚举i个9的要求了
				//尽管这样的情况出现后可能是不最优解,但是是不是最优解这个问题
				//我们可以继续枚举9的个数,让下一次循环操作来判断即可。
				//所以我们这里要插入一个8
				//在插入8之后,那么就是尽量多的多填9就好了。 
				if (lastt>=8)
				{
    
    
					bin*=10;
					lastt-=8;
					num=bin*8+num;
					while (lastt)
					{
    
    
						bin*=10;
						if (lastt>=9) lastt-=9,num=bin*9+num;
						else num=bin*lastt+num,lastt=0;	
					}
				}
				else
				{
    
    
					bin*=10;
					num=bin*lastt+num,lastt=0;
				}
				ans=min(ans,num);
			}
		}
完整代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int T,n,k,ans;

inline void solve()
{
    
    
	ans=1e18;
	for (register int now=0; now<=9; ++now)
	{
    
    
		if (now+k<=9)    //不进位时 
		{
    
    
			int last=n-(now*(k+1)+(1+k)*k/2);
			if (last<0 || last%(k+1)!=0) continue;
			last/=(k+1);
			int num=now,bin=1;
			while (last)
			{
    
    
				bin*=10;
				if (last>=9) last-=9,num=bin*9+num;
				else num=bin*last+num,last=0;
			}
			ans=min(ans,num);
		}
		else
		{
    
    
			int l=now; int len1=9-l+1;
			int r=(k+1)-len1-1; int len2=r-0+1;  
			//没有进位时的个位表示为:now-->9 ; 
			//进位后的个位表示为:0-->len2
			int last=n-(now*len1+(0+len1-1)*len1/2);
			if (last<0) continue;
			last=last-(0+r)*(r+1)/2;
			if (last<0) continue;
			
			for (register int i=0; i<=17; ++i)
			{
    
    
				int lastt=last+len2*9*i-len2;
				//由进位时,sum(a)-i*9+1=sum(b)得到
				//我们就把这个-i*9+1加回去,这样求得的答案就是第一个数的 最高位到十位了 
				if (lastt%(len1+len2)!=0) continue;
				lastt/=(len1+len2);
				if (lastt<9*i) continue;
				
				int num=now,bin=1;
				for (register int j=1; j<=i; ++j) 
				{
    
    
					bin*=10;
					lastt-=9;
					num=bin*9+num;
				}
				//一段连续的9以后,如果剩下的数位和还是比9大,那么我们是不可以填9了
				//不然就不满足枚举i个9的要求了
				//尽管这样的情况出现后可能是不最优解,但是是不是最优解这个问题
				//我们可以继续枚举9的个数,让下一次循环操作来判断即可。
				//所以我们这里要插入一个8
				//在插入8之后,那么就是尽量多的多填9就好了。 
				if (lastt>=8)
				{
    
    
					bin*=10;
					lastt-=8;
					num=bin*8+num;
					while (lastt)
					{
    
    
						bin*=10;
						if (lastt>=9) lastt-=9,num=bin*9+num;
						else num=bin*lastt+num,lastt=0;	
					}
				}
				else
				{
    
    
					bin*=10;
					num=bin*lastt+num,lastt=0;
				}
				ans=min(ans,num);
			}
		}
	}
	if (ans==1e18) puts("-1");
	else printf("%lld\n",ans);
}

signed main(){
    
    
	scanf("%lld",&T);
	while (T--)
	{
    
    
		scanf("%lld%lld",&n,&k);
		solve();
	}
return 0;	
}

猜你喜欢

转载自blog.csdn.net/Dove_xyh/article/details/108558090