zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:reverse(数位DP)

    题目描述

    我们定义:

    $overline{d_k...d_2d_1}=sum limits_{i=1}^kd_i imes {10}^{i-1}=n(d_iin [0,9] and d_iin 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$,请求出下面这个集合的大小:

    ${ nin Z |Lleqslant nleqslant R and Lleqslant 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$的整数次幂)
    令$1leqslant Lleqslant Rleqslant N$。


    题解

    $20\%$算法:

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

    时间复杂度:$Theta(T imes(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 imes$状态数$ imes 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
    ",calc(R)-calc(L-1));
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    回溯算法之素数环
    回溯算法之迷宫问题
    回溯算法之0-1背包问题
    贪心算法之搬桌子问题
    贪心算法之背包问题
    动态规划之收集苹果
    动态算法
    c#数和二叉树
    c# 排序算法
    c#栈的习题2
  • 原文地址:https://www.cnblogs.com/wzc521/p/11366308.html
Copyright © 2011-2022 走看看