T1
题目描述:
一开始你有一个数S,你可以通过两种操作改变这个值:
1.给这个数加a
2.给这个数乘b
问最少多少步操作可以使S变成T。
输入格式:
一行4个数,分别代表S,T,a,b。
数据范围:
1<S<T<1018
1<a<1018
2<b<1018
这道题比较恶心了,看到1018的数据范围可能就会想到这是不是数学,然后搜刮脑子里那少的可怜的数学公式或定理(我就是这么做的,然后浪费了好长时间QAQ)
不过我在手模样例的时候有了这么一个发现:
好像所有的情况都能化简成最后的这个式子即:
哦!天哪,这个wa太不吉利了(疯狂暗示)(˘•ω•˘)
不过这样我们就可以贪心的解决这道题了
while(s*b<t) s*=b,k++; 找到一个最大的k,使得bkS<T,然后 (T - bkS) / a找出W。
这样数据就都全了,可是怎么求解最小步数呢?
乘K次b肯定跑不了了,接下来要加多少次a呢?
我们可以每次都 用W%b来获得最末尾项加a的次数,然后用 W / b来把倒数第二位的项变为最末尾的项,如下图:
什么!你问如果Ci >b 怎么办?你可以想一想,如果它大于b,那你把它放到前一项里不是会更优吗?所以贪心的假定每一个C都小于b得到的就是优的结果。
好,那咱们现在把题交上去。。。。。
噫~好!我WA了!φ(≧ω≦*)♪
现在咱们来考虑一下到底是哪出锅了。(*/ω\*)
还是比较明显的,问题就出在这里 (T - bkS) / a不一定是个整数,也就是说,你找的这个最大的K其实不对,它没办法构成T。
不过好在解决办法也很简单——把每个K都找一遍不就行了,就是b是最小的2,那K等于100时它也有1267650600228229401496703205376,这个数远超最大数据1018
好!咱们再交一遍。。。。。。。
噫~好!我又WA了( ゚∀゚) ,究其原因无非就是1018太大了,很容易爆long long,这时候判断K就不能用 s*b<t 了,而是要改用 while( s<t / b)(我觉得其实应该加上等于号的,但是不加也过了)
这次再交就没有问题了。( • ̀ω•́ )✧
最后附上代码和注释:
1 #include<bits/stdc++.h> 2 using namespace std; 3 long long s,t,a,b; 4 long long ans = 1e18+7;//先赋一个大值 方便取min 5 int main() 6 { 7 cin>>s>>t>>a>>b;//读入数据 8 9 if( (t-s)%a==0 ) //就是不乘b,纯用a加 10 ans = (t-s)/a;//如果能加出来就记录一下 11 12 long long k=0;//k就是指数 13 while( s<t/b )//防止爆longlong的优化 14 { 15 k++;//寻找每个k 16 s*=b;//寻找每个k下的b(k)s 17 if((t-s)%a!=0) continue;//没办法用a凑出t,那这个k肯定就不对了 18 19 long long now = k;//方便处理 20 long long w = (t-s)/a;//找到 W 21 22 for(int i = 1; i<=k;i++)//循环多项式的每一项,找出常数C 23 { 24 now += w%b;//加上这个常数C 25 w/=b;//把倒数第二位的项变成最后一项 26 } 27 ans = min(ans,now+w);//在每种情况的k下寻找最小的步数 28 //now+w是因为最后除完b会可能会剩下最高次项的常数 ,如果不剩下加个0也无所谓 29 } 30 cout<<ans;//输出结果 31 }
( • ̀ω•́ )✧感谢大佬Accoty_AM的帮助
(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤为美好的代码献上祝福
(o°ω°o)CSP(NOOP)RP++