[数位DP] BZOJ 4521 [Cqoi2016]手机号码

题解:http://www.cnblogs.com/ccz181078/p/5379967.html


数位DP和喜闻乐见的前缀和


#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;

inline char nc()
{
	static char buf[100000],*p1=buf,*p2=buf;
	if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
	return *p1++;
}

inline void read(ll &x){
	char c=nc(),b=1;
	for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
	for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

ll L,R;
ll f[15][10][3][2][2][2];

inline ll Calc(ll x){
	int num[16],p=0;
	ll ret=0,itmp=x;
	while (itmp) num[++p]=itmp%10,itmp/=10;
	for (int i=1;i<p;i++)
		for (int j=1;j<10;j++)
			for (int a=0;a<3;a++)
				ret+=f[i][j][a][1][0][0]+f[i][j][a][1][1][0]+f[i][j][a][1][0][1];
	for(int i=1;i<num[p];i++)
        for(int a=0;a<3;a++)
        	ret+=f[p][i][a][1][0][0]+f[p][i][a][1][0][1]+f[p][i][a][1][1][0];
    int p1=num[p],p2=0;
    int d4=p1==4,d8=p1==8,l3=0;
    for(int i=p-1;i;i--){
        for(int j=0;j<num[i];j++)
		{
            for(int a=0;a<3;a++)
			{
                ret+=f[i][j][a][1][0][0];
                if(!d4)ret+=f[i][j][a][1][0][1];
                if(!d8)ret+=f[i][j][a][1][1][0];
                if(l3){
                    ret+=f[i][j][a][0][0][0];
                    if(!d4)ret+=f[i][j][a][0][0][1];
                    if(!d8)ret+=f[i][j][a][0][1][0];
                }
            }
            if(l3)continue;
            if(p1==j){
                ret+=f[i][j][1][0][0][0];
                if(!d4)ret+=f[i][j][1][0][0][1];
                if(!d8)ret+=f[i][j][1][0][1][0];
                if(p1==p2)
				{
                    ret+=f[i][j][0][0][0][0];
                    if(!d4)ret+=f[i][j][0][0][0][1];
                    if(!d8)ret+=f[i][j][0][0][1][0];
                }
            }
        }
        if(p1==p2 && p1==num[i]) l3=1;
        p2=p1; p1=num[i];
        if(p1==4) { if(d8) break; d4=1; }
        if(p1==8) { if(d4) break; d8=1; }
    }
    return ret;
}

int main()
{
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	for (int i=0;i<10;i++) f[1][i][0][0][i==4][i==8]=1;
	for (int i=2;i<=12;i++)
		for (int j=0;j<10;j++)
			for (int k=0;k<10;k++)
				for (int d4=0;d4<2;d4++)
					for (int d8=0;d8<2;d8++)
						if (j!=k)
						{
							for (int w=0;w<3;w++)
							{
								f[i][j][0][0][d4||j==4][d8||j==8]+=f[i-1][k][w][0][d4][d8];
								f[i][j][0][1][d4||j==4][d8||j==8]+=f[i-1][k][w][1][d4][d8];
							}
						}
						else
						{
							
                            f[i][j][1][0][d4||j==4][d8||j==8]+=f[i-1][k][0][0][d4][d8];
                            f[i][j][1][1][d4||j==4][d8||j==8]+=f[i-1][k][0][1][d4][d8];
                            f[i][j][2][1][d4||j==4][d8||j==8]+=f[i-1][k][1][0][d4][d8];
                            f[i][j][2][1][d4||j==4][d8||j==8]+=f[i-1][k][1][1][d4][d8];
                            f[i][j][2][1][d4||j==4][d8||j==8]+=f[i-1][k][2][1][d4][d8];
						}
	read(L); read(R);
	printf("%lld\n",Calc(R+1)-Calc(L));
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值