网上的好多题解没有看懂,题解中的一些"显然"的结论也觉得完全不显然…
我们如果枚举这个数列的第一个数字的个位,那么,如果存在这个数列的话,是一定可以构造出这个数列的。(因为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;
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;
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;
}
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;
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;
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;
}
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;
}