CQOI2016 bzoj4521 手机号码

题意:


给定区间 [L,R] ,求区间内满足以下条件的数的个数
1.不能同时含有4,8
2.必须至少三位相邻位的数码相同
例如11125877157满足条件而11148521234,11010110110不满足。
题解在下面




















题解:


这个一眼数位动规。用递推写我认为需要勇气……所以我是记搜的。
想想需要哪些属性?
因为当前位的状态肯定和是否出现4、8有关,所以我们设isF, isE来表示 是否出现4,是否出现8。
又因为我需要连续相同这个属性,所以我们可以想到维护p2, p1,分别表示当前位+2,当前位+1这两个数位上是什么数。
同时,因为可能之前满足了相等性质但是现在看不出来,所以我们加入Req表示是否满足相等性质。
这里我认为前导零可以加以处理。我用10表示前导0,规避了一些判断。

代码:

#include<cstdio>
#include<cstring>
using namespace std;

typedef long long LL;
LL Dp[15][12][12][2][2][2],bit[15];

LL Solve1(LL A,LL B)
{   LL i,j,t,len,p2,p1,Ans=0;
    bool isF,isE,Req,flag;
    for(i=A;i<=B;i++)
    { t=i;len=0;
      while(t>0){bit[++len]=(t%10LL);t/=10LL;}
      p2=p1=10;Req=0;isF=isE=0;
      for(j=len;j>=1;j--)
      { isF=isF||(bit[j]==4LL);
        isE=isE||(bit[j]==8LL);
        if(isF&&isE)break;
        if(p2!=10&&p2==p1&&p1==bit[j])
         Req=1;
        p2=p1;p1=bit[j];
      }
      if(Req&&j==0)Ans++;//printf("%lld\n",i);}
    }
    return Ans;
}

LL DFS(int len,int p2,int p1,bool isF,bool isE,bool Req,bool flag)
{   if(isF&&isE)return 0;
    if(!len)
    { if(Req)return 1;
      return 0;
    }
    if(!flag&&Dp[len][p2][p1][isF][isE][Req]!=-1)
     return Dp[len][p2][p1][isF][isE][Req];

    int Max,Ni;bool nR;LL Tmp=0;
    if(flag)Max=bit[len];else Max=9;
    for(int i=0;i<=Max;i++)
    { if(p1==10&&i==0)Ni=10;else Ni=i;
      nR=Req||(p2!=10&&p2==p1&&p1==i);
      Tmp+=DFS(len-1,p1,Ni,isF||(i==4),isE||(i==8),nR,flag&&(i==Max));
    }

    if(!flag)Dp[len][p2][p1][isF][isE][Req]=Tmp;
    return Tmp;
}
LL Solve2(LL x)
{   int len=0;
    while(x>0){bit[++len]=(x%10LL);x/=10LL;}
    return DFS(len,10,10,0,0,0,1);
}

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

    LL L,R;
    memset(Dp,-1,sizeof(Dp));
    scanf("%lld%lld",&L,&R);
    if(R-L<=10000)printf("%lld\n",Solve1(L,R));
    else printf("%lld\n",Solve2(R)-Solve2(L-1));

    fclose(stdin);fclose(stdout);
    return 0;
}

你可能感兴趣的:(数位动规-OI-题解)