[CSP校内集训]reverse(数位DP)

ぃ、小莉子 提交于 2019-12-02 06:45:18

题意

给一个范围[L,R],求满足\(L\leq n \leq R\)\(L\leq rev(n) \leq R\)\(n\)的个数,其中\(rev(n)\)表示将\(n\)翻转\((123->321)\),多组询问\((L,R\leq 2^64-1)\)

思路

长这样的计数问题,没什么悬念考虑数位DP

只设\(f_{i,lim}\)表示处理到第\(i\)位是否顶上界似乎不太够,因为在转移过程中还需要知道倒过来的数的状态,所以再用个变量\(lim2\)记录倒过来的数的状况

因为从前向后确定数倒过来看是从后向前确定数,所以\(lim2\)有三个值:比当前上界小,顶上界,暂时大于上界,用\(0,1,2\)表示

仅仅这样做并不能很好的处理前缀0的情况(如0001倒过来看会变成1000,但实际上应该为1),所以我还用了一个变量\(zer\)表示第一个非零数出现的位置

状态即为\(f_{i,lim1,lim2,zer}\),一遍数位DP即可求出答案,递归边界返回值为1的条件为\(lim2 \neq 2\) 或 倒过来的限制R的位数比L多1

本题毒瘤,\(L,R\)会炸longlong,需要用unsigned longlong来做,随时都需要注意爆边界的情况qwq

考试的标程都是错的几个意思

#include<bits/stdc++.h>
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef unsigned long long ll;
int T,t1,t2,d;
ll L,R,wei[25];
ll dp[70][23][2][3];//第一个不是0的位置 
//0:没碰到上界,1:顶上界,2:过上界 

template <class T>
void read(T &x)
{
    char c; int sign=1;
    while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
int gw(ll x,int w)
{
    if(w==20) return 1;
    return (x/wei[w-1])%10;
}
int get_dep(ll x)
{
    int ret=0;
    while(x) {++ret; x/=10;}
    return ret;
}
ll dfs(int stp,int zer,int lim1,int lim2,ll l,ll r)//正数第stp位,第一个非零位置,顶上界,逆向 
{
    if(stp==d+1) return ((lim2!=2 || (stp-zer<=19 && zer && r/wei[stp-zer]) )  &&  zer);
    if(dp[stp][zer][lim1][lim2]!=-1) return dp[stp][zer][lim1][lim2];
    ll ret=0;
    int x=gw(l,d-stp+1),y=gw(r,zer?stp-zer+1:1);
    if(lim1)
    {
        int l2,l3=0;
        for(int i=0;i<=x;++i)
        {
            if(i>y) l2=2;
            else if(i==y) l2=(lim2==2 ? 2 : 1);
            else l2=0;
            if(i) l3=stp;
            ret+=dfs(stp+1,zer?zer:l3,i==x,l2,l,r);
        }
    }
    else
    {
        int l2,l3=0;
        for(int i=0;i<=9;++i)
        {   
            if(i>y) l2=2;
            else if(i==y) l2=(lim2==2 ? 2 : 1);
            else l2=0;
            if(i) l3=stp;
            ret+=dfs(stp+1,zer?zer:l3,0,l2,l,r);
        }
    }
    return dp[stp][zer][lim1][lim2]=ret;
}
ll solve(ll L,ll R)//翻转前<=L,翻转后<=R
{
    if(!L||!R) return 0;
    memset(dp,-1,sizeof(dp));
    d=get_dep(L);
    return dfs(1,0,1,1,L,R);
}
int main()
{
    freopen("reverse.in","r",stdin);
    freopen("reverse.out","w",stdout);
    read(T);read(t1);read(t2);
    wei[0]=1;
    for(int i=1;i<=19;++i) wei[i]=wei[i-1]*10;
    while(T--)
    {
        read(L);read(R);
        cout<<solve(R,R)+solve(L-1,L-1)-solve(R,L-1)-solve(L-1,R)<<endl;
    }
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!