实用指南:洛谷P9560 [SDCPC 2023] Math Problem 题解
[SDCPC 2023] Math Problem
这题题解竟然没有满!
思路
有没有发现两个操作貌似相反?大家发现,假设先进行第一个操作再进行第二个管理,相当于没有进行管理。故最优的操作顺序一定是先进行若干次操作二,再进行若干次处理一。
那么我们可以枚举两次操作的次数。第二次操作最多进行log k n \log_{k}{n}logkn次。如果初始为n nn,进行一次操作一,其可能的取值范围为[ n ⋅ k , n ⋅ k + k − 1 ] [n\cdot k,n\cdot k+k-1][n⋅k,n⋅k+k−1],进行两次操作一,其可能的取值范围为[ n ⋅ k ⋅ k , ( n ⋅ k + k − 1 ) ⋅ k + k − 1 ] [n\cdot k\cdot k,(n\cdot k+k-1)\cdot k+k-1][n⋅k⋅k,(n⋅k+k−1)⋅k+k−1]。倘若当前取值范围为[ l , r ] [l,r][l,r],那么执行一次操作一后可能范围即为[ l ⋅ k , r ⋅ k + k − 1 ] [l\cdot k,r\cdot k+k-1][l⋅k,r⋅k+k−1],该范围内的数都能取到。根据鸽巢原理,如果取值范围大于等于m mm,那么一定会有至少一个m mm的倍数,所以处理一最多执行log k m \log_{k}{m}logkm次,总复杂度O ( T log k n log k m ) O(T\log_{k}{n}\log_{k}{m})O(Tlogknlogkm)。
代码
有可能爆 long long,要开 __int128!
#include<bits/stdc++.h>
using namespace std;
int t,k,m,a,b;
long long p[70],cnt,n;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>t;
while(t--)
{
cin>>n>>k>>m>>a>>b;
cnt=1,p[1]=n;
while(k!=1&&n!=0)//p中记录所有操作二的可能结果
{
n/=k;
p[++cnt]=n;
}
long long ans=1e18;
for(int i=1;i<=cnt;i++)
{
if(k==1)//k=1要特判
{
if(p[i]%m==0)
ans=min(ans,1ll*(i-1)*b);
continue ;
}
__int128 l=p[i],r=p[i];
long long sum=1ll*(i-1)*b;
while(l/m==r/m&&l%m!=0&&r%m!=0)
{
l*=k,r*=k;
r+=k-1;
sum+=a;
}
ans=min(ans,sum);
}
if(ans!=1e18)
cout<<ans<<"\n";
else
cout<<-1<<"\n";
}
return 0;
}
浙公网安备 33010602011771号