[CSP-S模拟测试]:reverse(数位DP)

题目描述

我们定义:

$\overline{d_k...d_2d_1}=\sum \limits_{i=1}^kd_i\times {10}^{i-1}=n(d_i\in [0,9]\ and\ d_i\in Z)$

我们对于任何正整数,定义一个函数:

$reverse(\overline{d_1d_2...d_k})=\overline{d_k...d_2d_1})$

比如:$reverse(123)=321,reverse(1000)=1,reverse(520)=25$。

现在,给出两个正整数$L,R$,请求出下面这个集合的大小:

$\{ n\in Z |L\leqslant n\leqslant R\ and\ L\leqslant reverse(n)\leqslant R\}$


输入格式

第一行包含三个整数$T,a,b$分别表示测试数据组数,特殊性质$1$,特殊性质$2$(如果该组数据包含特殊性质$1$,则$a=1$,否则$a=0$;如果该组数据包含特殊性质$2$,则$b=1$,否则$b=0$)。
接下来$T$行每行包含两个整数$L,R$。


输出格式

对于每组数据,输出一行,包含一个整数表示答案。


样例

样例输入:

3 0 0
1 10
10 20
123 12345

样例输出:

10
1
9952


数据范围与提示

对于所有数据,$T=50$。
特殊性质$1$:$L=1$。
特殊性质$2$:$R={10}^k$(即所有$R$都是$10$的整数次幂)
令$1\leqslant L\leqslant R\leqslant N$。


题解

$20\%$算法:

暴力枚举就好啦,不做过多解释。

时间复杂度:$\Theta(T\times(R-L))$。

期望得分:$20$分。

实际得分:$20$分。

另外$20\%$算法:

满足两个特殊性质,考虑从这里入手,$L=1$就说明所有的数都可以,$R={10}^k$说明所有小于等于它的书也都可以,那么第$3,4$个测试点的答案就是$R$。

时间复杂度:$\Theta(T)$。

期望得分:$20$分。

实际得分:$20$分。

$100\%$算法:

发现我们可以计算出$[1,L-1]$中和$[1,R]$中符合条件的数的个数。

那么考虑数位$DP$,定义$dp[i][j]s_1][s_2]$表示 计算了$i$位,当前的前缀长度都是$j$,并且将前缀$reverse$后与$L$和$R$的后$j$位比较结果为$s_1,s_2$,后面的选择有多少种($s_1,s_2$表示大于,等于,小于)。

最后注意,数据范围是$2^{64}-1$,所以我们需要用到一个东西叫做$unsigned\ long\ long$。

时间复杂度:$\Theta(T\times$状态数$\times 10)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
unsigned long long L,R;
unsigned long long dp[22][22][3][3];
int pre_num1[22],pre_num2[22],pro_num[22];
unsigned long long dfs(int pos,int revp,int cmpl,int cmpr,bool lim)
{
	if(!pos)
	{
		if(revp<pre_num1[0]+1)cmpl=0;
		if(revp<pre_num2[0]+1)cmpr=0;
		return cmpl&&cmpr!=2;
	}
	if(dp[pos][revp][cmpl][cmpr]!=-1&&!lim)return dp[pos][revp][cmpl][cmpr];
	int flag=lim?pro_num[pos]:9;
	unsigned long long res=0;
	for(int i=0;i<=flag;i++)
	{
		int ncl,ncr;
		if(i==pre_num1[revp])ncl=cmpl;
		else ncl=i>pre_num1[revp]?2:0;
		if(i==pre_num2[revp])ncr=cmpr;
		else ncr=i>pre_num2[revp]?2:0;
		res+=dfs(pos-1,revp+1,ncl,ncr,lim&&i==flag);
	}
	if(!lim)dp[pos][revp][cmpl][cmpr]=res;
	return res;
}
unsigned long long calc(unsigned long long x)
{
	if(!x)return 0;
	memset(dp,-1,sizeof(dp));
	memset(pro_num,0,sizeof(pro_num));
	while(x)
	{
		pro_num[++pro_num[0]]=x%10;
		x/=10;
	}
	unsigned long long res=0;
	for(int i=pre_num1[0];i<=pro_num[0];i++)
	{
		int flag=i==pro_num[0]?pro_num[i]:9;
		for(int j=1;j<=flag;j++)
		{
			int ncl,ncr;
			if(j==pre_num1[1])ncl=1;
			else ncl=j>pre_num1[1]?2:0;
			if(j==pre_num2[1])ncr=1;
			else ncr=j>pre_num2[1]?2:0;
			res+=dfs(i-1,2,ncl,ncr,i==pro_num[0]&&j==flag);
		}
	}
	return res;
}
int main()
{
	int T,a,b;
	scanf("%d%d%d",&T,&a,&b);
	while(T--)
	{
		scanf("%llu%llu",&L,&R);
		unsigned long long l=L,r=R;
		memset(pre_num1,0,sizeof(pre_num1));
		memset(pre_num2,0,sizeof(pre_num2));
		while(l){pre_num1[++pre_num1[0]]=l%10;l/=10;}
		while(r){pre_num2[++pre_num2[0]]=r%10;r/=10;}
		printf("%llu\n",calc(R)-calc(L-1));
	}
	return 0;
}

rp++

posted @ 2019-08-16 20:55  HEOI-动动  阅读(241)  评论(0编辑  收藏  举报