zoukankan      html  css  js  c++  java
  • 「NOIP2005P」循环

    问题描述

           对于一个整数n的正整数次幂来说,它的后k位是否会发生循环?如果循环的话,循环长度是多少呢?

    输入格式
      只有一行,包含两个整数n(1 <= n < 10^100)和k(1 <= k <= 100),n和k之间用一个空格隔开,表示要求n的正整数次幂的最后k位的循环长度。
     
    输出格式
      包括一行,这一行只包含一个整数,表示循环长度。如果循环不存在,输出-1。
     
    数据规模和约定
      对于30%的数据,k <= 4;
      对于全部的数据,k <= 100。
     
    解题算法
    【模拟】
     
    解题历程:
     
    先暴力切掉30分
     
    大致是维护一个k位字符串不断乘上一个int型,
    出现与刚开始如出一辙的串式可以判断是出现循环了,输出;
    出现了出现过的串式(刚开始的除外)就可以看出是死循,输出(-1)
    【可能在数学上有更好的判断方式,但目前只能想到这样】
     
    至于判重,用了字符串哈希,膜一个大素数即可。
     
    就是一个高精度乘法
    代码:

    void check()
    {
        int rest=0;
        F(i,0,k-1)
        {
            int sub=(st[i]-'0')*n+rest;
            st[i]=sub%10+'0';
            rest=sub/10;
        }
        return;
    }

    int change(string st)
    {
        int x=0;
        F(i,0,k-1)
        x=(x*10+st[i]-'0')%mo;
        return x;
    }

    void work()
    {
        int cnt=0;
        while(1)
        {
            cnt++;
            check();
            if(st==ansst)
            {
                cout<<cnt<<endl;
                return;
            }    
            int num=change(st);
            if(vit[num]==1)
            {
                cout<<"-1"<<endl;
                return;
            }
            else vit[num]=1;
        }
        return ;
    }
    以上、
    这样30分。
     
    看了下数据,n远超Int 和ll 型,
    就码了一个真~高精度乘法
    好像还是第一次码...很坎坷
     
    代码:
    void check()
    {
        memset(sum,0,sizeof(sum));
        int rest=0;
        int len=n.size();
        F(t,0,len-1)
        {
            rest=0;
            F(i,0,k-1-t)
            {
                int sub=(st[i]-'0')*(n[t]-'0')+rest;
                sum[t+i]+=sub%10;
                rest=sub/10;
            }
        }
        rest=0;
        F(i,0,k-1)
        {    
            int sub=sum[i]+rest;
            st[i]=sub%10+'0';
            rest=sub/10;    
        }
        return;
    }
    以上、
     
    写的时候主要是字符类型和整数转换出了问题
    改了半天
     
    可能是写的有问题,分数上没有丝毫增长......
    优化解法今天头疼不想了,
    下次再写。
     
    以上
    2018.2.12
     
    --------------------------------------------------
    今天尝试把字符串哈希改成十进制的
    把取膜数微调一下
    分数没有上涨
    所以可能是算法的问题
    改了一个更简朴的,即直接记录串式,每次跟前面比较
     
    代码:
        int cnt=0;
        while(1)
        {
            cnt++;
            check();
            if(st==ansst[0])
            {
                cout<<cnt<<endl;
                return;
            }   
            int fflag=0;
            F(i,1,cnt_st)
            if(st==ansst[i])
            {
                fflag++;
                break;
            }
            if(fflag)
            {
                cout<<"-1"<<endl;
                return;
            }
            else ansst[++cnt_st]=st;
        }
    以上、
    TLE止于30
     
    毕竟每次记录时间复杂度太大
    之前哈希策略反而显得更优秀
    思而不得
     
    以上
    2018.2.13
    -------------------------------------------
     
    归来
    之前算法注定爆
    复杂度高+hash风险

    草稿纸画了画
    想出了新算法
     
    清晰可见的是
    后k位相等得数
    最后1,2,3...k位都等
     
    即k位循环一定出现在最后1,2,3...k-1位的循环上
     
    这就很清晰了
    不断乘(一定在9次以内)
    第一次出现末尾等即出现了循环【1】
     
    那么循环【2】一定是多个循环【1】拼接而成
     
    也就是说如果n*x=n[1],n*y=n[2]
    那么n[2]一定是n*x^a,或者说n[2]=n[1]*x^a-1
    即y=x^a
     
    可见需要记两个大整数 x 和n[1];
     
    代码:
    void work(int ist)
    {
        if(ist==k)
        {
            int sub=k-1;
            while(ans[sub]=='0'&&sub!=0)sub--;
            D(i,sub,0)cout<<ans[i];
            cout<<endl;
            return;
        }
        int aloncnt=0,sum=0;
        while(1)
        {
            //cout<<ist<<" "<<aloncnt<<" "<<subn<<" "<<st<<endl;
            //cout<<ans<<endl;
            if(aloncnt>9||(flag==true&&st[ist]==subn[ist]))break;
    //之所以先判断是巧妙地处理了n[a]=n[a+1]的情况 ,fllag是消除初始数与自身比较的bug
            st=check(st,n);
            anoy=check(anoy,n);
            ans=check_add(ans,last);
            flag=true;
            aloncnt++;
        }
        if(aloncnt>9)
        {
            cout<<"-1"<<endl;
            return ;
        }
        last=ans;
        if(aloncnt)n=anoy;
        work(ist+1);
    }
     
    以上
    AC
     
    当然了
    还有一个难点
     
    即记录ans值
    我当时对着数据没想清楚
     
    回家拿笔算了算
    大概就明了了
     
    即 当前将n[q]*x[q]^a[q]得到循环
    那么那么这次ans增添的值就是a[q]*上一个时刻的ans(用心悟能读懂)
     
    over
    以上
    2018.3.4
  • 相关阅读:
    SpringBoot前端模板
    Http协议与TCP协议简单理解
    Kafka简介、基本原理、执行流程与使用场景
    初学Kafka工作原理流程介绍
    Redis数据持久化、数据备份、数据的故障恢复
    zookeeper的分布式锁
    eclipse下将maven项目打包为jar(1.不带第三方jar,2.带第三方jar)
    Redis入门篇(安装与启动)
    java操作Redis缓存设置过期时间
    java单例模式实现
  • 原文地址:https://www.cnblogs.com/qswx/p/8445438.html
Copyright © 2011-2022 走看看