CQOI2016 bzoj4521 手机号码

来源:互联网 发布:php下载系统 编辑:程序博客网 时间:2024/05/09 23:55

题意:


给定区间[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;}
2 0