纪中集训2019.08.21JZOJ6315数字

Posted hansue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了纪中集训2019.08.21JZOJ6315数字相关的知识,希望对你有一定的参考价值。

题目链接

 

题意:

  设$s(i)$为将$1\sim i$看做字符串后依次连接形成的串。给定正整数$n$,求最小的$i$使得$n$是$s(i)$的字串。$T$组数据。

  $n\le 10^17, \; t\le 10^4$

 

分析:

  不能模拟$s(i)$的组成过程来找答案,时间不能承受。

  也不能预处理$s(k)$,空间不能承受。

  那就只能在$n$上找答案。

  以下把数字当成字面量来讨论,更方便。

  同时,这里讨论的前缀和后缀不包括本身。

  思考一下,答案分为三种:

  1.$ans=n$

  2.$n$由$ans$的前缀和$ans-1$的后缀组成

  3.$n$由$ans-k$的后缀和$ans-k+1 , \, ans-k+2 \dots ans-2 , \, ans-1$整串和$ans$的前缀组成

  那么把三类答案的所有可能取最小值即可。

  

  先确定一下我们模拟的工具。用字符串还是数字?

  字符串更方便控制位置,数字在比较、增减的时候更方便。

  我这里用一种取两家之长的做法:

  1.以五倍最大长度($N=17$)存储每一位数字

  2.用头尾指针记录数字的位置,初始化数字首位在$2*N+1$上。非数字位数值为$-1$,比较时视作通配符。

  3.定义左移、右移、找零、判九等函数辅助操作

  个人感觉这个模型的灵活度很高,适用于数字$\times$字符串的操作。

 

  现在看到第二类答案。

  首先枚举“切一刀”的位置,设分离出的数字为$x$和$y$,将$y$从最左边开始向右卡位,尝试匹配。

  边界一:$y$的右侧不能和$x$的右侧平齐。连续的自然数数值是有变化的,如果两个数字的右侧平齐,甚至$y$的右侧越过$x$的右侧,$x$必然不是$ans-1$的后缀。

  边界二:$y$的左侧不能和$x$的左侧平齐。这和我们的定义相违背:$ans$相邻的数字不是$n$的子串。

  注意,这里的“匹配”,有可能有进位。

 

  看到第三类答案。

  首先枚举基准数,再将它的相邻数字往左、右匹配。这里凸显出了数据模型的灵活性。

  基准数不能等于原数。

  数字减少时可能会出现$0$,注意应对。

  注意数字位数的变化,左右移动的距离要根据当前数字的长度来。

 

实现(100分):

 

技术图片
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define IL inline
using namespace std;
typedef long long LL;
const int N=17;

IL void swap(LL &x,LL &y)
    LL t=x;    x=y;    y=t;
    


    int T;
    LL n;

struct Num
    LL s[N*5+1];
    int p,q;
    
    IL LL operator[](int i)
        return s[i];
        
    
    
    IL int len()
        return q-p+1;
        
    
    
    IL bool err()
        if(p>q)
            return true;
        if(s[p]==0)
            return true;
        for(int i=p;i<=q;i++)
        if(s[i]==-1)
            return true;
        return false;
        
    
    
org;

IL Num fill(LL x)
    Num a;
    memset(a.s,-1,sizeof a.s);
    int l=0;
    while(x>0)
        a.s[++l]=x%10LL;
        x/=10LL;
        
    
    for(int i=1;i<=l/2;i++)
        swap(a.s[i],a.s[l-i+1]);
    
    for(int i=l;i>=1;i--)
        a.s[2*N+i]=a.s[i];
    for(int i=1;i<=2*N;i++)
        a.s[i]=-1;
    a.p=2*N+1;    a.q=2*N+l;
    
    return a;
    


IL LL val(Num a)
    LL ret=0;
    for(int i=a.p;i<=a.q;i++)
        ret=ret*10LL+a[i];
    return ret;
    


IL Num copy(Num a,int l,int r)
    for(int i=a.p;i<=a.q;i++)
    if(i<l||i>r)
        a.s[i]=-1;
    a.p=l;    a.q=r;
    return a;
    


IL Num shl(Num a,int k)
    int p1=a.p-k,q1=a.q-k;
    for(int i=a.p;i<=a.q;i++)
        a.s[i-k]=a[i];
    for(int i=q1+1;i<=a.q;i++)
        a.s[i]=-1;
    a.p=p1;    a.q=q1;
    return a;
    


IL Num shr(Num a,int k)
    int p1=a.p+k,q1=a.q+k;
    for(int i=a.q;i>=a.p;i--)
        a.s[i+k]=a[i];
    for(int i=a.p;i<p1;i++)
        a.s[i]=-1;
    a.p=p1;    a.q=q1;
    return a;
    


IL bool eql(Num a,Num b,int l,int r)
    for(int i=l;i<=r;i++)
    if(a[i]==-1||b[i]==-1)
        continue;
    else 
    if(a[i]!=b[i])
        return false;
    return true;
    


IL bool operator<(Num a,Num b)
    return val(a)<val(b);
    


IL int f0(Num a)
    int ret=a.q;
    while(a[ret]==0)
        ret--;
    return ret+1;
    


IL bool all9(Num a,int l,int r)
    for(int i=l;i<=r;i++)
    if(a[i]!=9)
        return false;
    return true;
    


IL Num operator+(Num a,Num b)
    for(int i=b.p;i<=b.q;i++)
        a.s[i]=b[i];
    a.q=b.q;
    return a;
    


IL Num inc(Num a)
    int pos=a.q;
    a.s[pos]++;
    while(a[pos]==10)
        a.s[pos]=0;
        a.s[--pos]++;
        
    
    if(pos<a.p)
        a.s[--a.p]=1;
    return a;
    


IL Num dec(Num a)
    int pos=f0(a);
    a.s[pos-1]--;
    bool flag=false;
    if(a.p==pos-1&&a.s[pos-1]==0)
        flag=true;
        a.p++;
    
    for(int i=pos;i<=a.q;i++)
        a.s[i]=9;
    if(flag)
        a=shl(a,1);
    return a;
    


int main()
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);

    scanf("%d",&T);
    while(T--)
        scanf("%lld",&n);
        
        Num org,ans;
        org=ans=fill(n);
        int l=org.len();
        
        for(int pos=org.q;pos>org.p;pos--)
            if(org[pos]==0)
                continue;
            
            Num x=copy(org,org.p,pos-1);
            Num y=copy(org,pos,org.q);
            
            y=shl(y,l);
            while(y.q<x.q&&y.p<x.p)
                int pos0=f0(y);
                
                if(eql(x,y,y.p,pos0-2)
                    &&all9(x,pos0,x.q)
                    &&(x[pos0-1]==-1
                     ||x[pos0-1]+1==y[pos0-1]))
                    Num z=copy(y,y.p,y.q);
                    for(int i=y.q+1;i<=x.q;i++)
                        z.s[i]=0;
                    z.q=x.q;
                    if(eql(y,z,y.p,y.q))
                        ans=min(ans,z);
                    
                
                
                if(eql(x,y,y.p,y.q))
                    Num z=inc(y+copy(x,y.q+1,x.q));
                    if(eql(y,z,y.p,y.q))
                        ans=min(ans,z);
                    
                
                
                y=shr(y,1);
                
            
            
        
        
        for(int i=org.p;i<=org.q;i++)
            for(int j=i;j<=org.q;j++)
                if(j-i+1>=l)
                    continue;
                if(org[i]==0)
                    continue;
                
                Num x=copy(org,i,j);
                bool flag=true;
                while(flag)
                    x=dec(x);
                    x=shl(x,x.len());
                    if(x.q<org.p)
                        break;
                    
                    if(x.err()||!eql(x,org,x.p,x.q))
                        flag=false;
                    
                
                x=copy(org,i,j);
                while(flag)
                    x=inc(x);
                    x=shr(x,x.len());
                    if(x.p>org.q)
                        break;
                    
                    if(!eql(x,org,x.p,x.q))
                        flag=false;
                    
                
                
                if(x.p>org.q)
                    x=dec(x);
                
                if(flag)
                    ans=min(ans,x);
                    
            
        
        
        printf("%lld\n",val(ans));
        
    

    return 0;

View Code

 

 

小结:

  应对神奇大模拟,优秀的模型往往会有事半功倍的效果。

 

以上是关于纪中集训2019.08.21JZOJ6315数字的主要内容,如果未能解决你的问题,请参考以下文章

纪中模拟2019.08.03JZOJ1308取数游戏

纪中模拟2019.08.17JZOJ3503粉刷

木兮的纪中集训感想

纪中集训2019.11.08

纪中集训2019.11.09

纪中集训的第七天