首页 > 试题广场 >

饥饿的小易

[编程题]饥饿的小易
  • 热度指数:20589 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
小易总是感觉饥饿,所以作为章鱼的小易经常出去寻找贝壳吃。最开始小易在一个初始位置x_0。对于小易所处的当前位置x,他只能通过神秘的力量移动到 4 * x + 3或者8 * x + 7。因为使用神秘力量要耗费太多体力,所以它只能使用神秘力量最多100,000次。贝壳总生长在能被1,000,000,007整除的位置(比如:位置0,位置1,000,000,007,位置2,000,000,014等)。小易需要你帮忙计算最少需要使用多少次神秘力量就能吃到贝壳。

输入描述:
输入一个初始位置x_0,范围在1到1,000,000,006


输出描述:
输出小易最少需要使用神秘力量的次数,如果使用次数使用完还没找到贝壳,则输出-1
示例1

输入

125000000

输出

1
设f(x)=4x+3,g(x)=8x+7。
计算可以得到以下两个规律:
(1)  g(f(x))=f(g(x))   即f和g的执行顺序没有影响。

(2)  f(f(f(x)))=g(g(x))    即做3次f变换等价于做2次g变换

    由于规律(1) 对于一个可行方案,我们可以调整其变换的顺序。如ffggfggff,我们可以转化成 fffffgggg。
    由于规律(2),并且为了使执行次数最少,每3个f我们可以转化成2个g,如方案fffffgggg,可以转化成ffgggggg。
    因此一个最优的策略,f的执行次数只能为0,1,2。对于输入的x,我们只需要求x,4x+3,4(4x+3)+3的最小g执行次数就可以了。
 
发表于 2017-08-24 16:09:05 回复(13)

核心问题

  1. 4x+3和8x+7的数学操作,可以用二进制的左移和补1表示

    y = 4*x+3,相当于x的二进制左移2位,然后空位补1,即原先x的二进制为#####,则y的二进制为#####11,

    y = 8*x+3,相当于y的二进制左移3位,然后空位补1,即原先x的二进制为#####,则y的二进制为#####111

  2. 小易的移动,最终可以表达成4x+3操作进行了m次,8x+7操作进行了n次

    4*x+3操作进行m次,则x的二进制后面增加2m个1

    8*x+7操作进行n次,则x的二进制后面增加了3n个1

    小易的移动,最终可以表达为:x的二进制后面增加了(2m+3n)个1

    移动的顺序对其到达没有影响

  3. 小易移动次数的分析

    初始位置为0,则直接满足,需移动0次

    初始位置不为0,则记times = (2m+3n),m取1到10_0000,n取1到10_0000

    所以,times的取值范围为[2,30_0000]。即:最多30_0000次搜索,就能获得结果。

  4. 由于幂次操作数值过大,需要作出变换。参考牛客网用户。

代码如下

import java.util.Scanner;

/**
 * Created by Halley on 2017/8/11.
 */
public class Main {
    public static final long LIMIT = 300000;//最多搜索次数
    public static final long N = 1000000007;//求余

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()) {
            System.out.println(sol(sc.nextLong()));
        }
    }

    //次数判定方法
    public static long sol(long in){
        //如果初始位置为0,则直接可行,返回0次
        if(in == 0){
            return 0L ;
        }else{//初始位置不为0,则开始搜索
            return search(in);
        }
    }

    //不为0时的搜索
    public static long search(long in){//参数:初始坐标
        long temp = in;
        //遍历,获取最小位移
        for(int i=1;i<=LIMIT;i++){
            //long temp = (in+1)*(long)Math.pow(2,i)-1;//当循环较大时,幂次太高,数字超出范围,报错
            //递推
            temp = (temp * 2 + 1 ) % N;
            if( temp % N == 0 ){
                //i是符合条件的最小偏移,然后对其进行分解
                for(int j =0;j<=(i / 2);j++){//j对应a值
                    if((i - 2*j) % 3 == 0){
                        return ((i+j)/3);
                    }
                }
            }
        }
        //超过最大次数还没匹配,则输出-1
        return -1L;
    }
}
发表于 2017-08-17 16:00:14 回复(13)
你们的办法都慢爆了!
居然都没人觉得每步2种情况,2^100000次种情况不可计算?(就算落在1e9+7的范围之内,扫完1e9+7也很慢了)还直接hash?——虽然直接hash是可以过……

做一下数学分析啊:
8x+7=8(x+1)-1
4x+3=4(x+1)-1
嵌套一下呢?
8(4x+3)+7=8((4(x+1)-1)+1 )-1=32(x+1)-1
继续往下,发现能生成的数很有规律哦!
利用这个规律,枚举很有限的次数就行了。
下面这份代码,用时<1ms,飞快好吗?


#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
#include <map> 
#include <queue>
using namespace std;
typedef long long ll;

const int mod=1e9+7;

int main(){
    int n;
    while(~scanf("%d",&n)){
        int times=4;
        int ans=100001;
        for(int i=1;i<=300000;++i){
            int x=((long long)(n)*times+times-1)%mod;
            if(x==0){
                ans=(i+1)/3+((i+1)%3?1:0);
                break;
            }
            times=times*2%mod;
        }
        printf("%d\n",ans>100000?-1:ans);
    }
    return 0;
}

编辑于 2016-08-08 23:39:35 回复(17)
用哈希表存储已访问的点,使用队列存储待访问的点,bfs广度优先遍历
其中乘法可以用移位代替。
注:说什么哈希慢,迭代次数2^100000次,我真是醉了,你既然都分析只需要迭代300000次就够了,还说什么2^100000次???实话告诉你,用我下面的方法同样是最多迭代300000次!!!并不是什么2^100000次。。。
另外,在真实考试的情景,能用10分钟搞定的题目绝不花15分钟甚至20分钟去想。你算算你在这题目上花了多少时间?最关键的是即便想了这么多,还不是要迭代300000次你才能有结果??
#include <bits/stdc++.h>
using namespace std;
#define MOD 1000000007LL
#define MAX 100000
int main(){
    unordered_map<long long,int> vist;
    for(long long x;cin>>x;vist.clear()){
        queue<long long> q;
        for(long long xx=vist[x]=1,q.push(x);q.size();){
            x = q.front();
            q.pop();
            if (x==0) break;
            if (vist[x]>MAX) continue;            
            xx=((x<<2)+3)%MOD;
            if (vist.find(xx)==vist.end()) {
                q.push(xx);
                vist[xx]=vist[x]+1;
            }
            xx=((x<<3)+7)%MOD;
            if (vist.find(xx)==vist.end()){
                q.push(xx);
                vist[xx]=vist[x]+1;
            }
        }
        cout<<(q.size()?vist[x]-1:-1)<<endl;
    }
    return 0;
}
既然如此,我贴一个计算迭代次数的代码,感兴趣的可以测试一下,是不是对于任何数字,都最多迭代300000次
#include <bits/stdc++.h>
#include <tr1/unordered_map>
using namespace std;
using namespace std::tr1;
#define MOD 1000000007LL
#define MAX 100000
#define ITERATOR 10000
int main(){
    unordered_map<long long,int> vist;
    for(long long i,n=1;1;++n,vist.clear()){
        i=rand()%MOD;   //伪随机数
        queue<long long> q;
        long long it=0,xx=1,x;
        for(vist[i]=1,q.push(i);q.size();++it){ //迭代次数
            x = q.front();
            q.pop();
            if (x==0) break;
            if (vist[x]>MAX) continue;           
            xx=((x<<2)+3)%MOD;
            if (vist.find(xx)==vist.end()) {
                q.push(xx);
                vist[xx]=vist[x]+1;
            }
            xx=((x<<3)+7)%MOD;
            if (vist.find(xx)==vist.end()){
                q.push(xx);
                vist[xx]=vist[x]+1;
            }
        }
        //cout<<(q.size()?vist[x]-1:-1)<<endl;
        if (it>ITERATOR) cout<<n<<" x="<<i<<", iterate="<<it<<endl;
    }
    return 0;
}

下面是我的部分运行结果,第一列是标号,第二列是输入的x,第三列是迭代次数。

最后奉劝大家,多思考是对的,但是思考一半就说别人的方法慢,迭代2^100000次,就很low了。
编辑于 2016-08-10 11:55:24 回复(13)
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
const long long mod=1000000007;
struct node{
    long long x,step;
    node(long long x,long long step):x(x),step(step){}
};
int main(){
    long long x;
    while(scanf("%lld",&x)!=EOF){
        queue<node> Q;
        map<long long,int> book;
        Q.push(node(x,0)),book[x]=1;
        long long res=-1;
        while(!Q.empty()){
            node head=Q.front();Q.pop();
            if(head.x%mod==0){
                res=head.step;
                break;
            }
            if(head.step>100000) break;
            long long x1=(4*head.x+3)%mod,x2=(8*head.x+7)%mod;
            if(book.count(x1)==0){
                book[x1]=1;
                Q.push(node(x1,head.step+1));
            }
            if(book.count(x2)==0){
                book[x2]=1;
                Q.push(node(x2,head.step+1));
            }
        }
        printf("%lld\n",res);
    }
}//直接bfs就可以啦~

发表于 2017-09-01 10:51:17 回复(5)

原理也是这个

  1. 4x+3=4(x+1)-1
  2. 8x+7=8(x+1)-1

式子1套式子1
4(4x+3)+3=16(x+1)-1
(x+1)前的数字是这种规律
4,8,16,32....
转换下 2^2,2^3,2^4....

现在我可以列出所有的公式
4(x+1)-1
8(x+1)-1
16(x+1)-1
...

把初始位置套进每个公式,再与1000000007取模就能知道结果
那么怎么知道小易用了几次超能力?


假设32(x+1)-1这个公式中了,其实也就是2^4*(x+1)-1中了。

<thead></thead>
2的N次幂 超能力使用次数
2^2 1
2^3 1
2^4 2
2^5 2
2^6 2
2^7 3
。。 。。

可以发现使用此时是N除3并向上取整

import sys
x0=int(sys.stdin.readline().strip())
count=0
s=[]
key=-1
fac=2
for i in range(1,3*100000):
    if (fac*(x0+1)-1)%1000000007==0:
        key=i
        break
    #防止fac过大,所以要取模下
    fac=fac*2%1000000007
if key==-1:
    print(-1)
else:
    print((key-1)//3+1)
编辑于 2017-09-14 09:28:38 回复(1)
x = int(input())
a = 2 # next number is 2**a(x+1)-1
if x % 1000000007 == 0:
    print(0)
else:
    x = (4*x +3) % 1000000007
    while x and a < 300001:
        x = (x*2 +1)% 1000000007
        a += 1
    if x:
        print(-1)
    else:
        print((a+2)//3)
无需解释了吧,看注释
发表于 2017-10-20 09:40:41 回复(0)

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Scanner;

public class Main{
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);		
		
		while(in.hasNext()){
			int x=in.nextInt();
			Map<Long, Integer> map=new HashMap<Long, Integer>();
			
			Queue<Long> queue=new LinkedList<Long>();
			queue.offer((long)x);
			map.put((long)x, 1);

			while(!queue.isEmpty()){
				long n=queue.poll();
				if(n==0) {System.out.println(map.get(n)-1); return;}
				if(map.get(n)>=100001) continue;
				
				if(!map.containsKey((4*n+3)%1000000007)){
					map.put((4*n+3)%1000000007, map.get(n)+1);
					queue.offer((4*n+3)%1000000007);
				}
				if(!map.containsKey((8*n+7)%1000000007)){
					map.put((8*n+7)%1000000007, map.get(n)+1);
					queue.offer((8*n+7)%1000000007);
				}
			}
			System.out.println(-1);
		}
	}
}


发表于 2016-08-19 15:46:44 回复(7)

一个简单的方法

#include 
using namespace std;
int main(){
    long long x0;
    long long res=-1;
    long long a=1000000007;
    cin>>x0;
    if(x0%a==0)
        return 0;
    x0=(x0*4+3)%a;
    int i=3;
    while(i<=300000){
        x0=(x0*2+1)%a;
        if(x0==0){
            if(i%3==0)
                res=i/3;
            else
                res=i/3+1;
            break;
        }
        i++;
    }
    cout<<res<<endl;
    return 0;
}
编辑于 2018-09-19 10:53:12 回复(0)
#include<iostream>
using namespace std;
int main()
{
    int rst = -1;
    long long x0 = 0, n1 = 1, n2 = 1, n0 = 0;
    cin >> x0;
    n0 = x0 + 1;
    n1 = (x0 + 1)*4;
    n2 = (x0 + 1)*16;
    for(int k = 0; k<=100000;k++)
    {
        if((n0 - 1)%1000000007 == 0)
        {
            rst = k;
            break;
        }
        if((n1 - 1)%1000000007 == 0 && k <= 99999)
        {
            rst = k + 1;
            break;
        }
        if((n2 - 1)%1000000007 == 0 && k<= 99998)
        {
            rst = k + 2;
            break;
        }
        n0 %= 1000000007;
        n1 %= 1000000007;
        n2 %= 1000000007;
        n0 *= 8;
        n1 *= 8;
        n2 *= 8;
    }
    cout << rst;
    return 0;
}

发表于 2018-07-20 20:49:07 回复(0)
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
long int startPos;
const long int birthPos = 1000000007;
while (std::cin >> startPos)
{
int result = -1;
int power = 100000;
if (!(startPos % birthPos))//如果当前位置能吃到,直接输出0
{
std::cout << result;
continue;
}

if (!((startPos * 4 + 3) % birthPos) || !((startPos * 8 + 7) % birthPos))//如果一步能吃到,就输出1;
{
result = 1;
std::cout << result;
continue;
}
std::vector<long int> V3{16*startPos + 15, 32 * startPos + 31,64 * startPos + 63 };//用一个向量保存每一层余数
for (int  i = 2; i < power; i++)
{
for (int j = 0; j < V3.size(); j++)
{
if (!(V3[j] % birthPos))
{
result = i;
}
else
{
V3[j] = V3[j] % birthPos;//求余,防止溢出,道理自己推断
V3[j] = 8 * V3[j] + 7;//下一次的位置;
}
}
if (result != -1)
{
std::cout << result;
break;
}
}
if(-1 == result)
{
std::cout << result << std::endl;
}
}
}
减掉多余的,每一层就只用三个数
编辑于 2018-07-11 17:25:17 回复(0)

找规律,迭代时间复杂度太高

#include<stdio.h>

/** 受各位大神指点,用数学公式做**/ 
int main()
{
    int i=0;
    long long zs=2, x=0, temp=0;
    scanf( "%lld", &x);

    for( i=2; i<100000*3; i++)
    {
        zs=(zs*2)%1000000007;
        temp=(zs*(x+1)-1)%1000000007;

        if( temp==0)
        {
            if( i%3==0) printf("%d", i/3);
            else printf("%d", i/3+1);
            break;
        }
    }
    if( i==100000*3) printf( "-1");

    return 0;
}
发表于 2018-04-03 17:49:54 回复(0)
// 其实这道题没有想象中那么复杂,是一道纯数学问题。看评论区又是bfs,又是哈希表……确实
// 高深,这里提供一种纯数学方法,高深数据结构不懂的可以看下。有两个结论必须要通过两个
// 关系式退出来,不然真的不好做。我当初做这个题看到大数据基本思路就出来了,但是奈何关
// 系式没找对,后来看了评论区的那个Le(全名遗忘)的公式解析顿悟了。照抄它的内容就是
// 说以下两个关系式很重要:(设f(x) = 4x + 3; g(x) = 8x + 7)
// 1.由于 f(g(x)) = g(f(x)) ,因此这个先进行 f(x) 还是先进行 g(x) 对结果无影响;
// 2.由于f(f(f(x))) = g(x),因此如果需要找最少步数,f(x) 次数做少为 0 而最大为 2。当 f(x) 次数
// 大于 2 时是可以转化为 0 - 2 的,多余的可以通过 g(x) 替换。
// 这两点很重要,有了这两点结论就可以写代码了。思路如下:
// 1.由于 f(x) 直接决定最后输出,这样只要在 g(x) 最大遍历(即题中给出100000)下一次查看即
// 可。这样,本程序的最大查询迭代次数为 3 * 100000 = 300000;
// 2.循环进行 f(x) 求解。0 1 2,求解后的结果分别作为 g(x) 的输入进行求解。由于迭代次数确定
// ,只需要注意数据不溢出即可。因此通过求余方法进行计算。即每次只计算小易当前位置离 10
// 00000007 位置是否为 0 即可。
// 语言表达能力有限,但是并不难理解。实际耗时 3 ms。代码如下:
#include <iostream>
using namespace std;
const long long STEP = 1000000007;
int main(void)
{
    bool flag(1); //这个是为了在循环中强制跳出
    long long x, dist, cont;
    cin >> x;
    for (int i = 0; i < 3 && flag == 1; i++) //所有f(x)可能性
    {
        dist = x;       //没找到重置初始位置信息
        cont = 0;       //没找到就要重置计数
        for (int j = 0; j < i; j++) //先进行f(x)求解,对应0 1 2次
        {
            dist = (4 * dist + 3) % STEP; //结果就是下次的步进数,想不明白可以画个图
            cont++;
            if (dist == 0)
            {
                flag = 0;
                break;
            }
        }
        if (flag == 1)
        {
            while (cont <= 100000 && flag == 1) //设置最大次数
            {
                dist = (8 * dist + 7) % STEP; //与f(x)求解方法一样
                cont++;
                if (dist == 0)
                {
                    flag = 0;
                    break;
                }
            }
        }
    }
    if (cont <= 100000)
        cout << cont << endl;
    else
        cout << -1 << endl;
    return 0;
}
发表于 2018-02-09 13:12:35 回复(0)

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int mod = (int) (1e9+7);
        int count = -1;
        int time = 4;
        for(int i = 1;i <= 300000;i++){
            long x = (time * (long)(n) + time -1) % mod; //注意long所在的位置,很关键
            //long x=((long )(n)*time+time-1)%mod;
            if(x == 0){
                count = (i+1)/3 + ((i+1)%3 != 0 ? 1:0);
                /*if((i+1) %3 != 0){
                    count = (i+1)/3 + 1;
                }
                else {
                    count = (i+1)/3;
                }*/
                break;
            }
            time = (time*2) % mod;
        }
    
            System.out.println(count);
    }
}

发表于 2017-12-10 21:25:53 回复(1)
参考toraoh的评论
遍历数列 x[i] = 2**i*(X+1)-1, i>=2.
x[i+1] = 2x[i]+1; 对应的模 a[i+1] = (2*a[i]+1)%m
x = int(input())
res, m = 100001, 1000000007
if x%m==0: res = 0
else:
    ai = (2*(x+1)-1)%m
    for i in range(2, 300001):
        ai = (2*ai+1)%m
        if ai==0:
            res = i//3+(i%3!=0)
            break
print(res if res<=100000 else -1)

编辑于 2017-09-13 17:20:27 回复(0)
基于 陈丽丰 的思路实现:
import java.util.Scanner;

public class Main {
	
	static int mod = 1000000007;
	static int LIMIT = 100000;
	
	public static void main(String[] args) {
		
		Scanner in = new Scanner(System.in);
		long x = in.nextLong();
		int cnt = 0;
		for(int i=0; i<LIMIT; i++){
			x %= mod;
			if(x == 0){
				System.out.println(cnt);
				return;
			}else{
				if(g(g(x)) > mod){
					long tmp = x;
					for(int j=1; j<=2; j++){
						tmp = f(tmp);
						if(tmp % mod == 0){
							System.out.println(cnt+j);
							return;
						}
					}
				}
                x = g(x);
				cnt++;
			}
		}
		System.out.println("-1");
	}
	
	private static Long f(long x){
		return 4*x+3;
	}
	
	private static Long g(long x){
		return 8*x+7;
	}

}

编辑于 2017-08-29 17:31:59 回复(2)
BFS广搜,注意对搜索过的位置进行标记,根据BFS的性质,之前到达的位置的步数一定比之后到达该位置的步数要小,所以相当于每个位置搜索一次就可以了,最多搜索的步数是100000次。这里用map进行了位置的记录。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#include<queue>
using namespace std;
const long long Mod=1000000007;
struct node
{
    long long x;
    int sum;
};
map<long long,int>Map;
void bfs(long long sx)
{
    queue<node>Q;
    node Node;
    Node.x=sx;Node.sum=0;
    Q.push(Node);
    while(!Q.empty())
    {
        if(Map[0]!=0) return;
        node tmp=Q.front();
        Q.pop();
        node newNode;
        newNode.x=(4*tmp.x+3)%Mod;
        newNode.sum=tmp.sum+1;
        if(Map[newNode.x]==0)
        {
            Map[newNode.x]=newNode.sum;
            if(newNode.sum>=100000) return;
            if(newNode.x!=0) Q.push(newNode);
        }
        newNode.x=(8*tmp.x+7)%Mod;
        newNode.sum=tmp.sum+1;
        if(Map[newNode.x]==0)
        {
            Map[newNode.x]=newNode.sum;
            if(newNode.sum>=100000) return;
            if(newNode.x!=0) Q.push(newNode);
        }
    }
    return ;
}
int main()
{
    long long n;
    cin>>n;
    if(n%Mod==0)
    {
        cout<<"0"<<endl;
        return 0;
    }
    bfs(n);
    if(Map[0]==0) cout<<"-1"<<endl;
    else cout<<Map[0]<<endl;
    return 0;
}


发表于 2017-08-16 12:36:08 回复(0)
#include <iostream>
#include<bits/stdc++.h>
#include<queue>
#include <map>
#define mod 1000000007LL
using namespace std;
//int vis[200000000]={0};
int main()
{   
  map<long long,int> vis;
queue<long long >q;
int   n;
long long xx;
 
while(cin>>n)
{   
int count=0;
int ok=0;
vis.clear();
q.push(n);
vis[n]=1;
while(!q.empty())
{
long long a=q.front();
q.pop();
if(a==0){ok=1;count=vis[a];break;}
if(vis[a]>100000) continue;
xx=(4*a+3)%mod;
if(!vis[xx])
{
q.push(xx);
vis[xx]=vis[a]+1;
}
 
xx=(8*a+7)%mod;
 if(!vis[xx])
{
q.push(xx);
vis[xx]=vis[a]+1;
}
}
if(ok)cout<<count-1<<endl;
else cout<<-1<<endl;
}
return 0;
}

发表于 2016-09-25 18:49:11 回复(0)

速度不快但是思路简单。

4x + 3等于做了两次2x + 1, 8x + 7做了三次。

从起点开始令x0 = 2*x0 + 1,统计做了多少次2x + 1后模1000000007等于0

再把次数分解成若干个3与2的和,3的个数加上2的个数最小,不超过100000
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int x0 = in.nextInt();
        in.close();
        int count = 0;
        while (x0 != 0 && count <= 300000) {
            x0 = ((x0 << 1) + 1) % 1000000007;
            count++;
        }
        int res = (count + 2) / 3;
        System.out.println(res > 100000 ? -1 : res);
    }
}


编辑于 2018-03-28 23:22:33 回复(21)
x=x
f(x)=4x+3=4(x+1)-1
g(x)=8x+7=8(x+1)-1
所以有
f(g(x))=4(8x+7)+3=4(8(x+1)-1+1)-1=32(x+1)-1
g(f(x))=8(4x+3)+7=8(4(x+1)-1+1)-1=32(x+1)-1
f(g(x))=g(f(x))——(1)
因此先对于同时有4x+3和8x+7操作的情况,可以调个顺序。
然后f(f(x))=4(4(x+1)-1+1)-1=16(x+1)-1
f(f(f(x))=4(16(x+1)-1+1)-1=64(x+1)-1
……
f^n(x)=4^n*(x+1)-1
同理g^m(x)=8^m*(x+1)-1
令4^n=8^m,可以得到n=3,m=2
因此f(f(f(x)))=g(g(x))——(2),3次4x+3的结果和2次8x+7一样,为了次数最少我们要把所有3次的4x+3操作转为2次8x+7,即4x+3最多只能有2次
针对(1)(2),我们可以确定如果有解,解的情况肯定是:
1)没有4x+3,直接8x+7吃到
2)1次4x+3,然后反复8x+7
3)2次4x+3,然后反复8x+7
然后针对这三种情况跑一波数,跑到x能被1000000007整除就可以。

【1】数字太大
由于章鱼哥最后能到的地方高达2^300000*(1000000006+1)-1,longlong都装不下了,所以算到一半的时候我们要处理一下数字
假设a mod c = m,x mod c = n,那么有ax mod c = mn mod c
所以(ax+b)mod c = ((a mod c)*(x mod c)+b mod c) mod c
所以(4x+3)mod 1000000007 = (4x mod 1000000007)+3 mod 1000000007
g^m(x) mod 1000000007 = ((8^m) mod 1000000007 * (x+1) mod 1000000007)+1000000006 mod 1000000007
由上面我们也可以得到x=1000000006是永远吃不到的,可以先返回-1
因此计算下一个x时,先x+1,然后乘8,对1000000007取余,再加1000000006,对1000000007取余就是下一个x。不满足就反复上一步。
对于long形,mod前面的数足够装了

【2】不使用long型的设想
对于int来说,最大只能到2147483647,而1000000006乘个4或者8远远超出了int最大值。
所以如果就使用int,那么最多就只能乘2了
对f^n(x)=4^n(x+1)-1
4(x+1)-1 mod 1000000007=2*2*(x+1)+1000000006 mod 1000000007
(2*(x+1)不会超过int,因此先取余。)
=2*(2*(x+1) mod 1000000007)+1000000006 mod 1000000007
然后构建代码时有
x=x+1;
x=(x*2)%1000000007;
x=(x*2)%1000000007;
x=(x+1000000006)%1000000007;
这样子就得到了下一个x对1000000007取余后的结果,不会超过int最大值。
对8x+7也同理,也就是x*2那里从写2次变为写3次

完整代码(全程不用long):
#include<cstdio>
int x87(int x, int x43){
    if(x==1000000006)return 100005;    //永远都差1步吃到草的
    int count=0;
    while(x43>0 && x%1000000007!=0){
        x++;
        x=(x*2)%1000000007;
        x=(x*2)%1000000007;
        x=(x+1000000006)%1000000007;
        count++;
        x43--;
    }
    while(x%1000000007!=0){
        x++;
        x=(x*2)%1000000007;
        x=(x*2)%1000000007;
        x=(x*2)%1000000007;
        x=(x+1000000006)%1000000007;
        count++;
        if(count>100000)return 100005;    //次数用完,吃不到
    }
    return count;
}
int main(){
    int x,f1=0,f2=0,f3=0,res=0;
    scanf("%d",&x);
    f1=x87(x,0);
    f2=x87(x,1);
    f3=x87(x,2);
    if(f1<f2)res=f1;
    else res=f2;
    if(f3<res)res=f3;
    if(res>100004)printf("-1\n");
    else printf("%d\n",res);
    return 0;
}
发表于 2018-05-05 10:06:46 回复(0)

问题信息

难度:
75条回答 22680浏览

热门推荐

通过挑战的用户

查看代码