codeforces 251C Number Transformation(数论)

Number Transformation
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Little Petya likes positive integers a lot. Recently his mom has presented him a positive integer a. There's only one thing Petya likes more than numbers: playing with little Masha. It turned out that Masha already has a positive integer b. Petya decided to turn his number a into the number b consecutively performing the operations of the following two types:

  1. Subtract 1 from his number.
  2. Choose any integer x from 2 to k, inclusive. Then subtract number (a mod x) from his number a. Operation amod x means taking the remainder from division of number a by number x.

Petya performs one operation per second. Each time he chooses an operation to perform during the current move, no matter what kind of operations he has performed by that moment. In particular, this implies that he can perform the same operation any number of times in a row.

Now he wonders in what minimum number of seconds he could transform his number a into number b. Please note that numbers x in the operations of the second type are selected anew each time, independently of each other.

Input

The only line contains three integers ab (1 ≤ b ≤ a ≤ 1018) and k (2 ≤ k ≤ 15).

Please do not use the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cincoutstreams or the %I64d specifier.

Output

Print a single integer — the required minimum number of seconds needed to transform number a into number b.

Sample test(s)
input
10 1 4
output
6
input
6 3 10
output
2
input
1000000000000000000 1 3
output
666666666666666667
Note

In the first sample the sequence of numbers that Petya gets as he tries to obtain number b is as follows: 10  →  8  →  6  →  4  →  3  →  2  →  1.

In the second sample one of the possible sequences is as follows: 6  →  4  →  3.

题意:

给你两个数ab (1 ≤ b ≤ a ≤ 1018) 和k(2 ≤ k ≤ 15).。要你把a变成b。你可以进行两种操作

1.把a-1.

2.a-(a%c).2<=c<=k.

每步只能执行两种操作的一种。c可以随意选择。

现在问你最少需要多少步把a变成b.

思路:

由于数据范围比较大不能简单的搜索。必须找找其他的规律了。

操作1没什么好说的。对于操作2.对于每个c肯定就是把a变成c的倍数了。设cm为2~k的最小公倍数。a=x*cm+y。当y=0时操作2已经无效了。此时必须使用操作1.使a减小最快的方法是先把a变成x*cm然后减-1.然后减成(x-1)*cm然后循环。用dp[i]表示。把i减成0需要的最小步数。那没执行一个上述操作需要的步数就为1+dp[cm-1]。所以可以直接计算出把a减到a-b<cm的最小步数。剩下就bfs把a变成b需要多少步就行了。

详细见代码:

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
const double PI=acos(-1.0);
const int maxn=370360;
int dp[maxn],k,vis[maxn],sp[maxn],head,tail;
long long q[maxn];
int gcd(int x,int y)
{
    int tp=x%y;
    while(tp)
    {
        x=y;
        y=tp;
        tp=x%y;
    }
    return y;
}
int dfs(int x)
{
    int tp=INF,i;
    if(dp[x]!=-1)
        return dp[x];
    for(i=2;i<=k;i++)
        if(x%i!=0)
            tp=min(dfs(x-x%i),tp);
    tp=min(dfs(x-1),tp);
    return dp[x]=tp+1;
}
int bfs(int st,int ed)
{
    int i;
    memset(vis,0,sizeof vis);
    head=tail=0;
    vis[st]=1;
    q[tail]=st;
    sp[tail++]=0;
    while(head<tail)
    {
        if(q[head]==ed)
            return sp[head];
        for(i=2;i<=k;i++)
        {
            if(!vis[q[head]-q[head]%i])
            {
                vis[q[head]-q[head]%i]=1;
                q[tail]=q[head]-q[head]%i;
                sp[tail++]=sp[head]+1;
            }
        }
        if(!vis[q[head]-1])
        {
            vis[q[head]-1]=1;
            q[tail]=q[head]-1;
            sp[tail++]=sp[head]+1;
        }
        head++;
    }
    return -1;
}
int main()
{
    int i,mc;
    long long a,b,df,ans,rep;

    while(~scanf("%I64d%I64d%d",&a,&b,&k))
    {
        mc=1,ans=0;
        for(i=2;i<=k;i++)
            mc=(mc*i)/gcd(mc,i);
        for(i=1;i<mc;i++)
            dp[i]=-1;
        dp[0]=0;
        for(i=1;i<mc;i++)
            dfs(i);
        if(a-a%mc>b)
            ans+=dp[a%mc],a-=a%mc;
        df=a-b;
        rep=df/mc;
        a=a-rep*mc;
        ans+=rep*(1+dp[mc-1]);
        df=a-b;
        if(df>0)
        {
            if(a%mc==0)
                ans+=bfs((a-1)%mc,b%mc)+1;
            else
                ans+=bfs(a%mc,b%mc);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值